Install required packages

if (!requireNamespace("BiocManager", quietly = TRUE))
    install.packages("BiocManager")
# BiocManager::install("TCGAbiolinks")

library(knitr)
library(ComplexHeatmap)
library(circlize)
library(limma)
library(Biobase)
library(tidyr)
library(dplyr)
library("edgeR")
library(kableExtra)

Introduction:

The dataset I have chosen to work for the assignment is titled Comprehensive analyses of B cell compartments across the human body reveal novel subsets and a gut resident memory phenotype. It can be found at https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE154583. This dataset analyizes B lineage cells in various tissues of multiple healthy organ donors which reveal tissue-specific differences and lack of correspondence between blood and tissues. New, functionally distinct memory B cell subsets were discovered, including one subset specific to the gut that has unique surface markers and a unique gene expression signature.

Overall design:

RNA profiling of memory B cell subsets defined by CD45RB and CD69 expression in spleen. There were 4 biological replicates. I will be analysing the samples to find differences between naive B cell and MBC (memory B cell) across the 4 donors.

The table below depicts the data set. The sample names legend is as follows: D = donor A/B/C/D = the donor name N = Naive cell type M = MBC [1-9] = sample number for that cell type from that donor Example: D.D.N.1 is the first sample from donor d of naive b cell type.

library(knitr)

normalized_count_data <- read.table(file=file.path(getwd(),"data", "ncmatrix.txt"),
                                    header = TRUE,sep = " ",
                                    stringsAsFactors = FALSE,
                                    check.names=FALSE)
kable(normalized_count_data[1:8,1:8], type="html")

D.A.N.1 D.A.N.2 D.A.N.3 D.A.N.4 D.A.M.1 D.A.M.2 D.A.M.3 D.A.M.4
A1BG 2.193523 1.776898 2.881219 2.037469 2.543096 3.391624 2.410621 2.466095
A2LD1 3.549519 2.724576 2.195215 3.880894 5.263618 6.122542 3.090540 3.861998
AAAS 19.303004 20.493553 20.854539 22.897273 16.677981 19.028332 19.161348 15.029221
AACS 5.344220 8.884488 5.716705 7.761787 8.457274 10.262966 9.889728 10.376212
AAGAB 41.038824 49.160834 43.904292 49.869484 45.361744 54.530268 48.336044 46.483567
AAK1 2.392934 24.639647 5.716705 14.262284 4.080783 4.096377 6.490134 3.955058
AAMP 26.322278 30.207260 33.522756 38.129781 35.485067 37.263818 32.079804 34.385742
AARS 53.083261 63.139096 57.258514 58.892562 45.243460 43.033983 69.969824 43.877882

NA

Initial heatmap

Create the heatmap matrix, i.e. matrix with data that will be used to create a heatmap of our data. The heatmap below is tscaled so that all the heat is not taken a single gene. Notice that the heatmap is very variable according to both cell type and the donor which it comes from. First, let’s try and group our samples according to cell type.

heatmap_matrix <- normalized_count_data[,
                        1:ncol(normalized_count_data)]
rownames(heatmap_matrix) <- rownames(normalized_count_data)
colnames(heatmap_matrix) <- colnames(normalized_count_data[,
                        1:ncol(normalized_count_data)])
library(ComplexHeatmap)
library(circlize)


# row normalise the matrix so that 1 dominant gene doesnt take all the heat.
heatmap_matrix <- t(scale(t(heatmap_matrix)))


# if all vals in heatmap are positive (i.e. min == 0) then use only red and white
if(min(heatmap_matrix) == 0){
    heatmap_col = colorRamp2(c( 0, max(heatmap_matrix)), 
                             c( "white", "red"))
  } else {
    heatmap_col = colorRamp2(c(min(heatmap_matrix), 0, 
          max(heatmap_matrix)), c("blue", "white", "red"))
  }
 # show dend sets the cluster points
current_heatmap <- Heatmap(as.matrix(heatmap_matrix),
                          
      show_row_dend = TRUE,show_column_dend = TRUE, 
      col=heatmap_col,show_column_names = TRUE, 
      show_row_names = FALSE,show_heatmap_legend = TRUE)

Heatmap


current_heatmap

I’ll sort the samples according to cell type (naive and MBC) and looked for CD69 signatures. I’m looking for CD69 signatures as that was the gene focused in the research paper linked to my dataset.

naive_samples <- grep(colnames(normalized_count_data),
                          pattern="N")
mbc_samples <- grep(colnames(normalized_count_data),
                          pattern="M")
gene_of_interest <- which(
  rownames(normalized_count_data) == "CD69")
gene_of_interest
[1] 1789

Model Design

Let’s take a look at the counts for CD69 in naive and MBC samples that we created above. Notice that there is a lot of variability in the data when we compare samples from different donors.


naive_samples <- t(normalized_count_data[gene_of_interest, naive_samples])
colnames(naive_samples) <- c("naive_samples")
naive_samples
         naive_samples
D.A.N.1       4427.407
D.A.N.2       4573.971
D.A.N.3       5270.619
D.A.N.4       4033.025
D.B.N.1.      5182.051
D.B.N.2       7704.061
D.B.N.3       4572.636
D.C.N.1      11602.214
D.C.N.2      16141.292
D.C.N.3       8459.910
D.D.N.1      17056.886
mbc_samples <- t(normalized_count_data [gene_of_interest, mbc_samples])
colnames(naive_samples) <- c("mbc_samples")
mbc_samples
             CD69
D.A.M.1  4841.642
D.A.M.2  5628.951
D.A.M.3  4777.727
D.A.M.4  4793.344
D.B.M.1  8812.249
D.B.M.2  7791.870
D.B.M.3  7958.720
D.B.M.4  7536.011
D.C.M.1 11237.375
D.C.M.2 11749.459
D.D.M.1 10111.073
D.D.M.2 10072.789
D.C.M.3 24949.251
D.C.M.4 15391.627
D.D.M.3 16954.914
D.D.M.4  8535.269
t.test(x=t(naive_samples),y=t(mbc_samples))

    Welch Two Sample t-test

data:  t(naive_samples) and t(mbc_samples)
t = -1.0085, df = 23.096, p-value = 0.3237
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -6035.378  2078.789
sample estimates:
mean of x mean of y 
 8093.097 10071.392 

There is not much variation in the T test so I’ll use limma to find a regression that explains our data. But first, let’s plot the data to see how samples are different when we compare them across donors and cell type.

samples_colors <- rainbow(7)
samples_colors <- unlist(lapply(samples_colors,FUN=function(x){rep(x,4)}))
limma::plotMDS(heatmap_matrix,
               col = samples_colors )

As we have observed so far through the plot, the variability in samples is very dependent on which donor it was extracted from. Additionally the b cell type also impacts how our differential will behave. so we have two groups - patients and cell type.

samples <- data.frame(
        lapply(colnames(normalized_count_data)[1:27], 
        FUN=function(x){
          unlist(strsplit(x, split = "\\."))[c(2,3)]}))
colnames(samples) <- colnames(normalized_count_data)[1:27]
rownames(samples) <- c("donors","cell_type")
samples <- data.frame(t(samples))
samples[4:10,]

#E Simple model Limma First let’s design a simple model to work on. The simple model only performs analysis on cell type and doesn’t look at differences in donors.

model_design <- model.matrix(~ samples$cell_type )
kable(model_design[1:5,], type="html")

(Intercept) samples$cell_typeN
1 1
1 1
1 1
1 1
1 0

#whenever cell type is mbc, intercept will be 0

I’ll make an expression matrix for the model which takes the normalised counts data and fits our data according to the simple model I defined above.

library(limma)
library(Biobase)
expressionMatrix <- as.matrix(normalized_count_data[,1:27])
rownames(expressionMatrix) <- 
  rownames(normalized_count_data)
colnames(expressionMatrix) <- 
  colnames(normalized_count_data)[1:27]
minimalSet <- ExpressionSet(assayData=expressionMatrix)

# Fit our data to the above model
fit <- lmFit(minimalSet, model_design)

# Apply empircal Bayes to compute differential expression for the above described model.
fit2 <- eBayes(fit,trend=TRUE)

Adding to my simpler model, I will be using the fit we got for our model design after emprical bayes. To support multiple hypothesis testing, I’ll be using the Benjamni - hochberg method to adjust the p values. The table below shows the top hits in our normalised counts, ordered according to P value. We’re only using top hits in the model for now, we’ll consider the lower hits while doing enrichment analysis.

# take our model and adjust it to support multiple hypothesis testing.
topfit <- topTable(fit2, 
                   coef=ncol(model_design),
                   adjust.method = "BH",
                   number = nrow(expressionMatrix))
output_hits <- merge(normalized_count_data[,1:1],
                     topfit,
                     by.y=0,by.x=1,
                     all.y=TRUE)
output_hits <- output_hits[order(output_hits$P.Value),]
library(tidyr)
library(dplyr)
names(output_hits)[names(output_hits) == "x"] <- "hgnc_symbol"
kable(output_hits[1:10,1:7],type="html",row.names = FALSE)

hgnc_symbol logFC AveExpr t P.Value adj.P.Val B
CD27 -214.714597 151.767119 -11.212276 0 1.0e-07 -0.8945546
FLT1 9.482993 6.904391 10.552987 0 2.0e-07 -0.9936763
CAMK2D 184.166052 125.269053 9.984270 0 4.0e-07 -1.0902537
MID1IP1 23.984151 13.517798 9.976335 0 4.0e-07 -1.0916810
WNT10A 16.275579 11.117717 9.372562 0 1.3e-06 -1.2073129
COL19A1 137.055412 84.289705 8.801016 0 3.3e-06 -1.3307825
BIN2 59.491041 44.540255 8.742137 0 3.3e-06 -1.3443497
MTSS1 141.469991 82.192824 8.734017 0 3.3e-06 -1.3462338
RHOBTB3 -32.451782 24.070331 -8.657583 0 3.4e-06 -1.3641248
SPRY1 51.980531 24.102699 8.624507 0 3.4e-06 -1.3719552


citation("dplyr")

To cite package ‘dplyr’ in publications use:

  Hadley Wickham, Romain François, Lionel Henry and Kirill Müller (2020). dplyr:
  A Grammar of Data Manipulation. R package version 1.0.2.
  https://CRAN.R-project.org/package=dplyr

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {dplyr: A Grammar of Data Manipulation},
    author = {Hadley Wickham and Romain François and Lionel {
             Henry} and Kirill Müller},
    year = {2020},
    note = {R package version 1.0.2},
    url = {https://CRAN.R-project.org/package=dplyr},
  }

-Calculate p-values for each of the genes in your expression set. How many genes were significantly differentially expressed? What thresholds did you use and why?

The number of genes which pass the threshold p-value in our top hits:I’m choosing the standard value 0.05 for the threshold. A lot of genes pass the threshold as well as correction so in some of the differentials ahead, i’ll choose a lower threshold.

#genes which pass threshold
length(which(output_hits$P.Value < 0.05))
[1] 2139
# genes which pass correction
length(which(output_hits$adj.P.Val < 0.05))
[1] 735

Donor model Limma

Now let’s correct our model to account for donor variability in our samples. I’ll create a new model for this and create a fit similar to what I did in the simple model. the table below shows how the new model is defined and what variables we’re considering. I predict that the new model will show us more accurate results, eliminating chance in our analysis.

model_design_d1 <- model.matrix(
  ~samples$donors + samples$cell_type)
kable(model_design_d1[1:5,1:5],type="html")
[WARNING] Could not convert TeX math 'donorsB </th>  <th style="text-align:right;"> samples', rendering as TeX:
  B </th>  <th style="text-align:right;"> 
                     ^
  unexpected '"'
  expecting "\\bangle", "\\brace", "\\brack", "\\choose", "\\displaystyle", "{", letter, digit, ".", "\\mbox", "\\text", "\\textbf", "\\textit", "\\textrm", "\\textsf", "\\texttt", "\\bm", "\\boldsymbol", "\\mathbb", "\\mathbf", "\\mathbfcal", "\\mathbffrak", "\\mathbfit", "\\mathbfscr", "\\mathbfsfit", "\\mathbfsfup", "\\mathbfup", "\\mathbold", "\\mathcal", "\\mathds", "\\mathfrak", "\\mathit", "\\mathrm", "\\mathscr", "\\mathsf", "\\mathsfit", "\\mathsfup", "\\mathtt", "\\mathup", "\\pmb", "\\symbf", "\\texttt", "\\sqrt", "\\surd", "\\mspace", "\\hspace", "\\mathop", "\\mathrel", "\\mathbin", "\\mathord", "\\mathopen", "\\mathclose", "\\mathpunct", "\\phantom", "\\boxed", "\\overset", "\\stackrel", "\\underset", "\\frac", "\\tfrac", "\\dfrac", "\\binom", "\\genfrac", "\\substack", "_", "^", "\\begin", "\\ensuremath", "\\bigg", "\\Bigg", "\\big", "\\Big", "\\biggr", "\\Biggr", "\\bigr", "\\Bigr", "\\biggl", "\\Biggl", "\\bigl", "\\", "\\left", "\\not", "!", "'", "''", "'''", "''''", "*", "+", ",", "-", ".", "/", ":", ":=", ";", "<", "=", ">", "?", "@", "~", "\\operatorname" or end of input
[WARNING] Could not convert TeX math 'donorsD </th>  <th style="text-align:right;"> samples', rendering as TeX:
  D </th>  <th style="text-align:right;"> 
                     ^
  unexpected '"'
  expecting "\\bangle", "\\brace", "\\brack", "\\choose", "\\displaystyle", "{", letter, digit, ".", "\\mbox", "\\text", "\\textbf", "\\textit", "\\textrm", "\\textsf", "\\texttt", "\\bm", "\\boldsymbol", "\\mathbb", "\\mathbf", "\\mathbfcal", "\\mathbffrak", "\\mathbfit", "\\mathbfscr", "\\mathbfsfit", "\\mathbfsfup", "\\mathbfup", "\\mathbold", "\\mathcal", "\\mathds", "\\mathfrak", "\\mathit", "\\mathrm", "\\mathscr", "\\mathsf", "\\mathsfit", "\\mathsfup", "\\mathtt", "\\mathup", "\\pmb", "\\symbf", "\\texttt", "\\sqrt", "\\surd", "\\mspace", "\\hspace", "\\mathop", "\\mathrel", "\\mathbin", "\\mathord", "\\mathopen", "\\mathclose", "\\mathpunct", "\\phantom", "\\boxed", "\\overset", "\\stackrel", "\\underset", "\\frac", "\\tfrac", "\\dfrac", "\\binom", "\\genfrac", "\\substack", "_", "^", "\\begin", "\\ensuremath", "\\bigg", "\\Bigg", "\\big", "\\Big", "\\biggr", "\\Biggr", "\\bigr", "\\Bigr", "\\biggl", "\\Biggl", "\\bigl", "\\", "\\left", "\\not", "!", "'", "''", "'''", "''''", "*", "+", ",", "-", ".", "/", ":", ":=", ";", "<", "=", ">", "?", "@", "~", "\\operatorname" or end of input
(Intercept) samples$donorsB </th> <th style="text-align:right;"> samples$donorsC samples$donorsD </th> <th style="text-align:right;"> samples$cell_typeN
1 0 0 0 1
1 0 0 0 1
1 0 0 0 1
1 0 0 0 1
1 0 0 0 0
  • Multiple hypothesis testing - correct your p-values using a multiple hypothesis correction method. Which method did you use? And Why? How many genes passed correction?

Adding to my new model, I will be using the fit we got for our model design after emprical bayes. To support multiple hypothesis testing, again, I’ll be using the Benjamni - hochberg method to adjust the p values. The table below shows the top hits in our normalised counts, ordered according to P value. We’re only using top hits in the model for now, we’ll consider the lower hits while doing enrichment analysis.


fit_d1 <- lmFit(minimalSet, model_design_d1)
fit2_d1 <- eBayes(fit_d1,trend=TRUE)


topfit_d1 <- topTable(fit2_d1, 
                   coef=ncol(model_design_d1),
                   adjust.method = "BH",
                   number = nrow(expressionMatrix))
output_hits_d1 <- merge(normalized_count_data[,1:1],
                         topfit_d1,by.y=0,by.x=1,all.y=TRUE)
output_hits_d1 <- output_hits_d1[order(output_hits_d1$P.Value),]
library(tidyr)
library(dplyr)
names(output_hits_d1)[names(output_hits_d1) == "x"] <- "hgnc_symbol"
kable(output_hits_d1[1:10,1:7],type="html",row.names = FALSE)

hgnc_symbol logFC AveExpr t P.Value adj.P.Val B
CD27 -215.966815 151.767119 -13.17385 0 0e+00 4.099368
FAM108B1 70.463306 92.981412 12.50266 0 0e+00 3.921982
ESYT1 77.261479 121.058561 11.15332 0 1e-07 3.495126
FLT1 9.414926 6.904391 11.00859 0 1e-07 3.442869
MID1IP1 23.655061 13.517798 10.96738 0 1e-07 3.427737
PFKL 55.718876 92.186806 10.91320 0 1e-07 3.407664
CAMK2D 188.270880 125.269053 10.89363 0 1e-07 3.400363
UXS1 14.974233 21.949910 10.52534 0 2e-07 3.257984
KLHL18 -29.483213 52.055266 -10.25772 0 3e-07 3.148262
ST3GAL1 89.618136 159.271675 10.25489 0 3e-07 3.147073

NA
NA

The number of genes which pass the threshold p-value in our top hits of the new model. Again the threshold value is chosen to be 0.05 here.

# correction test
length(which(output_hits_d1$P.Value < 0.05))
[1] 4240
length(which(output_hits_d1$adj.P.Val < 0.05))
[1] 2872

Comparing simple and donor models

The graph below compares the P values of different genes in both the models i.e. from when we only accounted for cell variablity vs when we added the donors. This might help us conduct a more accurate analysis and eliminate counts by chance.

simple_model_pvalues <- data.frame(gene_id =
  output_hits$hgnc_symbol,
  simple_pvalue=output_hits$P.Value)
pat_model_pvalues <-  data.frame(gene_id =
  output_hits_d1$hgnc_symbol,
  donor_pvalue = output_hits_d1$P.Value)
two_models_pvalues <- merge(simple_model_pvalues,
  pat_model_pvalues,by.x=1,by.y=1)
two_models_pvalues$colour <- "black"
two_models_pvalues$colour[
  two_models_pvalues$simple_pvalue<0.05] <- "orange"
two_models_pvalues$colour[
  two_models_pvalues$donor_pvalue<0.05] <- "blue"
two_models_pvalues$colour[
  two_models_pvalues$simple_pvalue<0.05 &
  two_models_pvalues$donor_pvalue<0.05] <- "red"

plot(two_models_pvalues$simple_pvalue,
     two_models_pvalues$donor_pvalue,
     col = two_models_pvalues$colour,
     xlab = "simple model p-values",
     ylab ="donor model p-values", 
     main="Simple vs donor Limma")

legend(0,1,legend=c("simple","donor", "both", "rest"),
       fill=c("orange","blue", "red", "black"),cex = 0.7)

Simple Vs Donor Model

The graph below compares the normalized counts for gene CD69 amongst different genes in both the models i.e. from when we only accounted for cell variablity vs when we added the donors.

cd69_gene <- rownames(normalized_count_data)[
  which(rownames(normalized_count_data) == "CD69")]
two_models_pvalues$colour <- "grey"
two_models_pvalues$colour[two_models_pvalues$gene_id==
                            cd69_gene] <- "red"
plot(two_models_pvalues$simple_pvalue,
     two_models_pvalues$donor_pvalue,
     col = two_models_pvalues$colour,
     xlab = "simple model p-values",
     ylab ="Donor model p-values",
      main="Simple vs Donor Limma")
points(two_models_pvalues[which(
  two_models_pvalues$gene_id == cd69_gene),2:3], 
       pch=20, col="red", cex=1.5)
legend(0,1,legend=c("CD69","rest"),
       fill=c("red","grey"),cex = 0.7)

I’ll now readjust my heatmap to reflect the genes in our donor model which have a p value threshold of 0.01. The below heatmap shows the top hits of the model which accounts for variability between donors in the samples.

top_hits <- output_hits_d1$hgnc_symbol[output_hits_d1$P.Value<0.01]
heatmap_matrix_tophits <- t(
  scale(t(heatmap_matrix[
    which(rownames(heatmap_matrix) %in% top_hits),])))
if(min(heatmap_matrix_tophits) == 0){
    heatmap_col = colorRamp2(c( 0, max(heatmap_matrix_tophits)), 
                             c( "white", "red"))
  } else {
    heatmap_col = colorRamp2(c(min(heatmap_matrix_tophits), 0, max(heatmap_matrix_tophits)),c( "blue","white", "red"))
  }
current_heatmap <- Heatmap(as.matrix(heatmap_matrix_tophits),
                           cluster_rows = TRUE,
                           cluster_columns = FALSE,
                               show_row_dend = TRUE,
                               show_column_dend = FALSE, 
                               col=heatmap_col,
                               show_column_names = TRUE, 
                               show_row_names = FALSE,
                               show_heatmap_legend = TRUE,
                         )
`use_raster` is automatically set to TRUE for a matrix with more than 2000 rows.
You can control `use_raster` arugment by explicitly setting TRUE/FALSE to it. Set
`ht_opt$message = FALSE` to turn off this message.
current_heatmap

Analysis with edgeR

Up until now I’ve been using limma for analysis but since the data expression type is bulk RNA seq, it’ll be better to try and use edgeR. Let’s use our donor model and make a heatmap according to the quasilikelihood method. The DGElist estimates dispersion and we can make a fit for the model like we did with Limma and ebayes. I’ve taken 13 as the threshold for count per millions following the edgeR protocol.

Filter counts

library("edgeR")
mc <- read.table(file=file.path(getwd(),"data","counts.txt"),
                                    header = TRUE,sep = " ",
                                    stringsAsFactors = FALSE,
                                    check.names=FALSE)
cpms = cpm(mc[, 1:27])
rownames(cpms) <- mc[,1]
# get rid of low counts
keep = rowSums(cpms >1) >=13  # following the edgeR protocol

counts_filtered = mc[keep,]
filtered_data_matrix <- as.matrix(counts_filtered[,1:27])
#set up the list
dgeList = DGEList(counts=filtered_data_matrix, group=samples$cell_type)

#uncomment the below line to view our dgeList
#dgeList
#Estimate dispersion in our donor model design
dgeList <- estimateDisp(dgeList, model_design_d1)

#fit the model according to Quasi likelihood method
fit <- glmQLFit(dgeList, model_design_d1)
library(kableExtra)
kable(model_design_d1[1:10,1:5], type="html") %>% row_spec(0, angle=45)

(Intercept) samples$donorsB samples$donorsC samples$donorsD samples$cell_typeN
1 0 0 0 1
1 0 0 0 1
1 0 0 0 1
1 0 0 0 1
1 0 0 0 0
1 0 0 0 0
1 0 0 0 0
1 0 0 0 0
1 1 0 0 0
1 1 0 0 0

NA
NA

Quasi-Likeli Method

Calculate the differential expression for the Quasi-likelihood method. I’m using cell type as the coefficient here from the intercept matrix above. The table below depicts the qlf model.

qlf.naive_vs_mbc <- glmQLFTest(fit, coef='samples$cell_typeN')
kable(topTags(qlf.naive_vs_mbc), type="html",row.names = FALSE)

logFC logCPM F PValue FDR
5.088891 4.405436 232.6046 0 0
4.932660 4.640855 230.2871 0 0
1.496842 6.669536 168.2487 0 0
3.819754 4.291068 159.3813 0 0
1.087937 4.475018 155.7020 0 0
2.770041 6.447053 154.3031 0 0
-5.387989 1.670861 144.8696 0 0
2.552735 7.016981 142.2133 0 0
1.294730 5.383999 141.1310 0 0
2.151244 2.840098 140.3316 0 0
x
BH
x
samples$cell_typeN
x
glm

NA

The qlf model has these many genes which pass the threshold P-value and FDR (0.05).

qlf_output_hits <- topTags(qlf.naive_vs_mbc,sort.by = "PValue",
                           n = nrow(normalized_count_data))

length(which(qlf_output_hits$table$PValue < 0.05))
[1] 4536
length(which(qlf_output_hits$table$FDR < 0.05))
[1] 3378

Comparing Limma and edgeR results

The graph below compares our differential results (genes which passed the threshold as well as correction) from limma and edgeR.

qlf_d_model_pvalues <- data.frame(
          hgnc_id = rownames(qlf_output_hits$table),
          qlf_d_pvalue=qlf_output_hits$table$PValue)
limma_d_model_pvalues <-  data.frame(
          hgnc_id = output_hits_d1$hgnc_symbol,
          limma_d_pvalue = output_hits_d1$P.Value)
two_models_pvalues <- merge(qlf_d_model_pvalues,
                            limma_d_model_pvalues,
                            by.x=1,by.y=1)
two_models_pvalues$colour <- "black"
two_models_pvalues$colour[two_models_pvalues$qlf_d_pvalue
                          <0.05] <- "orange"
two_models_pvalues$colour[two_models_pvalues$limma_d_pvalue
                          <0.05] <- "blue"
two_models_pvalues$colour[two_models_pvalues$qlf_d_pvalue
                          <0.05 & two_models_pvalues$limma_d_pvalue]  <- "red"
plot(two_models_pvalues$qlf_d_pvalue,
     two_models_pvalues$limma_patient_pvalue,
     col = two_models_pvalues$colour,
     xlab = "QLF donor model p-values",
     ylab ="Limma donor model p-values",
     main="QLF vs Limma")
legend(0,1,legend=c("QLF","Limma", "both", "rest"),
       fill=c("orange","blue", "red", "black"),cex = 0.7)

-Show the amount of differentially expressed genes using an MA Plot or a Volcano plot. Highlight genes of interest. Similar to the limma method let’s look at CD69 variations

cd69_gene <- rownames(normalized_count_data)[
  which(rownames(normalized_count_data) == "CD69")]
two_models_pvalues$colour <- "grey"
two_models_pvalues$colour[two_models_pvalues$hgnc_id
                          ==cd69_gene] <- "red"
plot(two_models_pvalues$qlf_d_pvalue,
     two_models_pvalues$limma_d_pvalue,
     col = two_models_pvalues$colour,
     xlab = "QLF patient model p-values",
     ylab ="Limma Patient model p-values",
     main="QLF vs Limma")
points(two_models_pvalues[
  two_models_pvalues$hgnc_id==cd69_gene,2:3],
       pch=24,  col="red", cex=1.5)
legend(0,1,legend=c("CD69","rest"),
       fill=c("red","grey"),cex = 0.7)

Now let’s make the heatmap according to our Quasi-likeli differential. This heatmap is divided properly between the two cell types (MBC samples on the left and naive samples on the right). Once again i’ve chosen the threshold value as 0.05.

top_hits <- rownames(qlf_output_hits$table)[output_hits_d1$P.Value <0.05] 
heatmap_matrix_tophits <- t(
  scale(t(heatmap_matrix[which(rownames(heatmap_matrix) 
                               %in% top_hits),]))) 
heatmap_matrix_tophits<- heatmap_matrix_tophits[,
  c(grep(colnames(heatmap_matrix_tophits),pattern = "M"),
    grep(colnames(heatmap_matrix_tophits),pattern = "N"))]
if(min(heatmap_matrix_tophits) == 0){
    heatmap_col = colorRamp2(c( 0, max(heatmap_matrix_tophits)), 
                             c( "white", "red"))
  } else {
    heatmap_col = colorRamp2(c(min(heatmap_matrix_tophits), 0, 
                               max(heatmap_matrix_tophits)), 
                             c("blue", "white", "red"))
  }
current_heatmap <- Heatmap(as.matrix(heatmap_matrix_tophits),
                           cluster_rows = TRUE,
                           cluster_columns = FALSE,
                               show_row_dend = TRUE,
                               show_column_dend = FALSE,
                               col=heatmap_col,
                               show_column_names = TRUE, 
                               show_row_names = FALSE,
                               show_heatmap_legend = TRUE, )
  • Visualize your top hits using a heatmap. Do you conditions cluster together? Explain why or why or not.
current_heatmap

This appears to be a much more refined heatmap than what we started with, I have formatedd the columns to be able to compare between cell types.

ORA

Thresholded lists

Number of Up regulated vs number of downregulated genes:

# up regulated
length(which(qlf_output_hits$table$PValue < 0.05 
             & qlf_output_hits$table$logFC > 0))
[1] 2938
# down regulated
length(which(qlf_output_hits$table$PValue < 0.05 
             & qlf_output_hits$table$logFC < 0))
[1] 1598

Prepare qlf output list

Let’s create a thresholded list of genes. Since we’ll be needing up-regulated and down-regulated files during enrichment analysis, we’ll store the tables to files


qlf_output_hits_withgn <- as.data.frame(qlf_output_hits)

qlf_output_hits_withgn[,"rank"] <- -log(qlf_output_hits_withgn$PValue, base = 10)*sign(qlf_output_hits_withgn$logFC)
qlf_output_hits_withgn <- qlf_output_hits_withgn[order(qlf_output_hits_withgn$rank),]
upregulated_genes <- rownames(qlf_output_hits_withgn)[
  which(qlf_output_hits_withgn$PValue < 0.05 
             & qlf_output_hits_withgn$logFC > 0)]
downregulated_genes <- rownames(qlf_output_hits_withgn)[
  which(qlf_output_hits_withgn$PValue < 0.05 
             & qlf_output_hits_withgn$logFC < 0)]
write.table(x=upregulated_genes,
            file=file.path("./data/upregulated_genes.txt"),sep = " ",
            row.names = FALSE,col.names = FALSE,quote = FALSE)
write.table(x=downregulated_genes,
            file=file.path("./data/downregulated_genes.txt"),sep = " ",
            row.names = FALSE,col.names = FALSE,quote = FALSE)
write.table(x=data.frame(genename= rownames(qlf_output_hits_withgn),F_stat=qlf_output_hits_withgn$rank),
            file=file.path("./data/ranked_genelist.txt"),sep = " ",
            row.names = FALSE,col.names = FALSE,quote = FALSE)

I used g:profiler web for over representation analysis. The three lists we created above: upregulated_genes, downregulated_genes and ranked_genelist were profiled through in the enrichment analysis.

I used the Benjamini-Hochberg FDR method for enrichment analysis and chose the threshold as 0.01.

The following data sources were used: GO biological process No electronic GO annotations Reactome WikiPathways

ORA results:

The threshold balue is 0.01 with a max term size of 10000.

###Upregulated Genes: Go Annotation Results: Fig 1a Go Annotations - upregulated genes

Reactome Annotation Results: Fig 1b Reactome annotations - upregulated genes

Wiki Pathways Annotation Results: Fig 1c Wiki Pathways annotations - upregulated genes

Down Regulated Genes

Go Annotation Results: Fig 1d Go Annotations - downregulated genes

Reactome Annotation Results: Fig 1e Reactome annotations - downregulated genes

Wiki Pathways Annotation Results: Fig 1f Wiki Pathways annotations - downregulated genes

Genes List:

Go Annotation Results: Fig 1g Go Annotations - gene rank list

Reactome Annotation Results: Fig 1h Reactome Annotations - gene rank list

Wiki Pathways Annotation Results: Fig 1i Wiki Pathways Annotations - gene rank list

Q&A

  • Which method did you choose and why?

I chose to use G: profiler to work with thresholded lists

  • What annotation data did you use and why? What version of the annotation are you using?

GO:BP – annotations: BioMart classes: releases/2020-12-08

REAC – annotations: BioMart classes: 2020-12-15

WP – 20201210

  • How many genesets were returned with what thresholds?

The threshold balue is 0.01 with a max term size of 10000.

Upregulted genes:

Go results: 99 genesets.

Reactome results: 27genesets

Wikipathways results: 6 genesets

Downregulted genes:

Go results: 143 genesets

Reactome results: 50 genesets

Wikipathways results: 4 genesets

Go results: 1209 genesets

Reactome results: 529 genesets

Wikipathways results: 154 genesets

  • Run the analysis using the up-regulated set of genes, and the down-regulated set of genes separately. How do these results compare to using the whole list (i.e all differentially expressed genes together vs. the up-regulated and down regulated differentially expressed genes separately)?

The results of all three analysis can be seen below

Up regulated Enrichment graph: Fig 1j Up regulated Enrichment graph

Downregulated list Enrichment graph: Fig 1k Downregulated list Enrichment graph

Rank list Enrichment graph: Fig 1l Rank list Enrichment graph

References

DataSet Paper

Weisel, N. M., Weisel, F. J., Farber, D. L., Borghesi, L. A., Shen, Y., Ma, W., Luning Prak, E. T., & Shlomchik, M. J. (2020). Comprehensive analyses of B-cell compartments across the human body reveal novel subsets and a gut-resident memory phenotype. Blood, 136(24), 2774–2785. https://doi.org/10.1182/blood.2019002782

Packages

Yihui Xie (2014) knitr: A Comprehensive Tool for Reproducible Research in R. In Victoria Stodden, Friedrich Leisch and Roger D. Peng, editors, Implementing Reproducible Computational Research. Chapman and Hall/CRC. ISBN 978-1466561595 citation(“ComplexHeatmap”)

Gu, Z. (2014) circlize implements and enhances circular visualization in R. Bioinformatics.

Ritchie, M.E., Phipson, B., Wu, D., Hu, Y., Law, C.W., Shi, W., and Smyth, G.K. (2015). limma powers differential expression analyses for RNA-sequencing and microarray studies. Nucleic Acids Research 43(7), e47.

strating high-throughput genomic analysis with Bioconductor. W. Huber, V.J. Carey, R. Gentleman, …, M. Morgan Nature Methods, 2015:12, 115.

Hadley Wickham (2020). tidyr: Tidy Messy Data. R package version 1.1.2. https://CRAN.R-project.org/package=tidyr

Hadley Wickham, Romain François, Lionel Henry and Kirill Müller (2020). dplyr: A Grammar of Data Manipulation. R package version 1.0.2. https://CRAN.R-project.org/package=dplyr

Robinson MD, McCarthy DJ and Smyth GK (2010). edgeR: a Bioconductor package for differential expression analysis of digital gene expression data. Bioinformatics 26, 139-140

Hao Zhu (2019). kableExtra: Construct Complex Table with ‘kable’ and Pipe Syntax. http://haozhu233.github.io/kableExtra/, https://github.com/haozhu233/kableExtra.

LS0tCnRpdGxlOiAiQXNzaWdubWVudCAyIgphdXRob3I6ICJJc2hhbiBTaGFybWEiCmRhdGU6ICIyMC8wMy8yMDIxIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMwoKLS0tCgoKSW5zdGFsbCByZXF1aXJlZCBwYWNrYWdlcwpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQogICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQojIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJUQ0dBYmlvbGlua3MiKQoKbGlicmFyeShrbml0cikKbGlicmFyeShDb21wbGV4SGVhdG1hcCkKbGlicmFyeShjaXJjbGl6ZSkKbGlicmFyeShsaW1tYSkKbGlicmFyeShCaW9iYXNlKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KCJlZGdlUiIpCmxpYnJhcnkoa2FibGVFeHRyYSkKCmBgYAojIEludHJvZHVjdGlvbjoKVGhlIGRhdGFzZXQgSSBoYXZlIGNob3NlbiB0byB3b3JrIGZvciB0aGUgYXNzaWdubWVudCBpcyB0aXRsZWQgQ29tcHJlaGVuc2l2ZSBhbmFseXNlcyBvZiBCIGNlbGwgY29tcGFydG1lbnRzIGFjcm9zcyB0aGUgaHVtYW4gYm9keSByZXZlYWwgbm92ZWwgc3Vic2V0cyBhbmQgYSBndXQgcmVzaWRlbnQgbWVtb3J5IHBoZW5vdHlwZS4gSXQgY2FuIGJlIGZvdW5kIGF0IGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvZ2VvL3F1ZXJ5L2FjYy5jZ2k/YWNjPUdTRTE1NDU4My4gVGhpcyBkYXRhc2V0ICBhbmFseWl6ZXMgIEIgbGluZWFnZSBjZWxscyBpbiB2YXJpb3VzIHRpc3N1ZXMgb2YgbXVsdGlwbGUgaGVhbHRoeSBvcmdhbiBkb25vcnMgd2hpY2ggcmV2ZWFsIHRpc3N1ZS1zcGVjaWZpYyBkaWZmZXJlbmNlcyBhbmQgbGFjayBvZiBjb3JyZXNwb25kZW5jZSBiZXR3ZWVuIGJsb29kIGFuZCB0aXNzdWVzLiBOZXcsIGZ1bmN0aW9uYWxseSBkaXN0aW5jdCBtZW1vcnkgQiBjZWxsIHN1YnNldHMgd2VyZSBkaXNjb3ZlcmVkLCBpbmNsdWRpbmcgb25lIHN1YnNldCBzcGVjaWZpYyB0byB0aGUgZ3V0IHRoYXQgaGFzIHVuaXF1ZSBzdXJmYWNlIG1hcmtlcnMgYW5kIGEgdW5pcXVlIGdlbmUgZXhwcmVzc2lvbiBzaWduYXR1cmUuCiAJCiMjIE92ZXJhbGwgZGVzaWduOgkKUk5BIHByb2ZpbGluZyBvZiBtZW1vcnkgQiBjZWxsIHN1YnNldHMgZGVmaW5lZCBieSBDRDQ1UkIgYW5kIENENjkgZXhwcmVzc2lvbiBpbiBzcGxlZW4uIFRoZXJlIHdlcmUgNCBiaW9sb2dpY2FsIHJlcGxpY2F0ZXMuIEkgd2lsbCBiZSBhbmFseXNpbmcgdGhlIHNhbXBsZXMgdG8gZmluZCBkaWZmZXJlbmNlcyBiZXR3ZWVuIG5haXZlIEIgY2VsbCBhbmQgTUJDIChtZW1vcnkgQiBjZWxsKSBhY3Jvc3MgdGhlIDQgZG9ub3JzLiAKClRoZSB0YWJsZSBiZWxvdyBkZXBpY3RzIHRoZSBkYXRhIHNldC4gVGhlIHNhbXBsZSBuYW1lcyBsZWdlbmQgaXMgYXMgZm9sbG93czoKRCA9IGRvbm9yCkEvQi9DL0QgPSB0aGUgZG9ub3IgbmFtZQpOID0gTmFpdmUgY2VsbCB0eXBlCk0gPSBNQkMKWzEtOV0gPSBzYW1wbGUgbnVtYmVyIGZvciB0aGF0IGNlbGwgdHlwZSBmcm9tIHRoYXQgZG9ub3IKRXhhbXBsZTogRC5ELk4uMSBpcyB0aGUgZmlyc3Qgc2FtcGxlIGZyb20gZG9ub3IgZCBvZiBuYWl2ZSBiIGNlbGwgdHlwZS4KYGBge3J9CmxpYnJhcnkoa25pdHIpCgpub3JtYWxpemVkX2NvdW50X2RhdGEgPC0gcmVhZC50YWJsZShmaWxlPWZpbGUucGF0aChnZXR3ZCgpLCJkYXRhIiwgIm5jbWF0cml4LnR4dCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLHNlcCA9ICIgIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcz1GQUxTRSkKa2FibGUobm9ybWFsaXplZF9jb3VudF9kYXRhWzE6OCwxOjhdLCB0eXBlPSJodG1sIikKCmBgYAoKIyBJbml0aWFsIGhlYXRtYXAKQ3JlYXRlIHRoZSBoZWF0bWFwIG1hdHJpeCwgaS5lLiBtYXRyaXggd2l0aCBkYXRhIHRoYXQgd2lsbCBiZSB1c2VkIHRvIGNyZWF0ZSBhIGhlYXRtYXAgb2Ygb3VyIGRhdGEuIFRoZSBoZWF0bWFwIGJlbG93IGlzIHRzY2FsZWQgc28gdGhhdCBhbGwgdGhlIGhlYXQgaXMgbm90IHRha2VuIGEgc2luZ2xlIGdlbmUuIE5vdGljZSB0aGF0IHRoZSBoZWF0bWFwIGlzIHZlcnkgdmFyaWFibGUgYWNjb3JkaW5nIHRvIGJvdGggY2VsbCB0eXBlIGFuZCB0aGUgZG9ub3Igd2hpY2ggaXQgY29tZXMgZnJvbS4gRmlyc3QsIGxldCdzIHRyeSBhbmQgZ3JvdXAgb3VyIHNhbXBsZXMgYWNjb3JkaW5nIHRvIGNlbGwgdHlwZS4gCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpoZWF0bWFwX21hdHJpeCA8LSBub3JtYWxpemVkX2NvdW50X2RhdGFbLAogICAgICAgICAgICAgICAgICAgICAgICAxOm5jb2wobm9ybWFsaXplZF9jb3VudF9kYXRhKV0Kcm93bmFtZXMoaGVhdG1hcF9tYXRyaXgpIDwtIHJvd25hbWVzKG5vcm1hbGl6ZWRfY291bnRfZGF0YSkKY29sbmFtZXMoaGVhdG1hcF9tYXRyaXgpIDwtIGNvbG5hbWVzKG5vcm1hbGl6ZWRfY291bnRfZGF0YVssCiAgICAgICAgICAgICAgICAgICAgICAgIDE6bmNvbChub3JtYWxpemVkX2NvdW50X2RhdGEpXSkKbGlicmFyeShDb21wbGV4SGVhdG1hcCkKbGlicmFyeShjaXJjbGl6ZSkKCgojIHJvdyBub3JtYWxpc2UgdGhlIG1hdHJpeCBzbyB0aGF0IDEgZG9taW5hbnQgZ2VuZSBkb2VzbnQgdGFrZSBhbGwgdGhlIGhlYXQuCmhlYXRtYXBfbWF0cml4IDwtIHQoc2NhbGUodChoZWF0bWFwX21hdHJpeCkpKQoKCiMgaWYgYWxsIHZhbHMgaW4gaGVhdG1hcCBhcmUgcG9zaXRpdmUgKGkuZS4gbWluID09IDApIHRoZW4gdXNlIG9ubHkgcmVkIGFuZCB3aGl0ZQppZihtaW4oaGVhdG1hcF9tYXRyaXgpID09IDApewogICAgaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMoIDAsIG1heChoZWF0bWFwX21hdHJpeCkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCAid2hpdGUiLCAicmVkIikpCiAgfSBlbHNlIHsKICAgIGhlYXRtYXBfY29sID0gY29sb3JSYW1wMihjKG1pbihoZWF0bWFwX21hdHJpeCksIDAsIAogICAgICAgICAgbWF4KGhlYXRtYXBfbWF0cml4KSksIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCiAgfQogIyBzaG93IGRlbmQgc2V0cyB0aGUgY2x1c3RlciBwb2ludHMKY3VycmVudF9oZWF0bWFwIDwtIEhlYXRtYXAoYXMubWF0cml4KGhlYXRtYXBfbWF0cml4KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgc2hvd19yb3dfZGVuZCA9IFRSVUUsc2hvd19jb2x1bW5fZGVuZCA9IFRSVUUsIAogICAgICBjb2w9aGVhdG1hcF9jb2wsc2hvd19jb2x1bW5fbmFtZXMgPSBUUlVFLCAKICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSxzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSkKCmBgYAoKIyMgSGVhdG1hcCAKYGBge3J9CgpjdXJyZW50X2hlYXRtYXAKYGBgCiBJJ2xsIHNvcnQgdGhlIHNhbXBsZXMgYWNjb3JkaW5nIHRvIGNlbGwgdHlwZSAobmFpdmUgYW5kIE1CQykgYW5kIGxvb2tlZCBmb3IgQ0Q2OSBzaWduYXR1cmVzLiBJJ20gbG9va2luZyBmb3IgQ0Q2OSBzaWduYXR1cmVzIGFzIHRoYXQgd2FzIHRoZSBnZW5lIGZvY3VzZWQgaW4gdGhlIHJlc2VhcmNoIHBhcGVyIGxpbmtlZCB0byBteSBkYXRhc2V0LiAKYGBge3J9Cm5haXZlX3NhbXBsZXMgPC0gZ3JlcChjb2xuYW1lcyhub3JtYWxpemVkX2NvdW50X2RhdGEpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm49Ik4iKQptYmNfc2FtcGxlcyA8LSBncmVwKGNvbG5hbWVzKG5vcm1hbGl6ZWRfY291bnRfZGF0YSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybj0iTSIpCmdlbmVfb2ZfaW50ZXJlc3QgPC0gd2hpY2goCiAgcm93bmFtZXMobm9ybWFsaXplZF9jb3VudF9kYXRhKSA9PSAiQ0Q2OSIpCmdlbmVfb2ZfaW50ZXJlc3QKYGBgCgojIE1vZGVsIERlc2lnbgpMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgY291bnRzIGZvciBDRDY5IGluIG5haXZlIGFuZCBNQkMgc2FtcGxlcyB0aGF0IHdlIGNyZWF0ZWQgYWJvdmUuIE5vdGljZSB0aGF0IHRoZXJlIGlzIGEgbG90IG9mIHZhcmlhYmlsaXR5IGluIHRoZSBkYXRhIHdoZW4gd2UgY29tcGFyZSBzYW1wbGVzIGZyb20gZGlmZmVyZW50IGRvbm9ycy4KCmBgYHtyfQoKbmFpdmVfc2FtcGxlcyA8LSB0KG5vcm1hbGl6ZWRfY291bnRfZGF0YVtnZW5lX29mX2ludGVyZXN0LCBuYWl2ZV9zYW1wbGVzXSkKY29sbmFtZXMobmFpdmVfc2FtcGxlcykgPC0gYygibmFpdmVfc2FtcGxlcyIpCm5haXZlX3NhbXBsZXMKCm1iY19zYW1wbGVzIDwtIHQobm9ybWFsaXplZF9jb3VudF9kYXRhIFtnZW5lX29mX2ludGVyZXN0LCBtYmNfc2FtcGxlc10pCmNvbG5hbWVzKG5haXZlX3NhbXBsZXMpIDwtIGMoIm1iY19zYW1wbGVzIikKbWJjX3NhbXBsZXMKYGBgCgpgYGB7cn0KdC50ZXN0KHg9dChuYWl2ZV9zYW1wbGVzKSx5PXQobWJjX3NhbXBsZXMpKQpgYGAKClRoZXJlIGlzIG5vdCBtdWNoIHZhcmlhdGlvbiBpbiB0aGUgVCB0ZXN0IHNvIEknbGwgdXNlIGxpbW1hIHRvIGZpbmQgYSByZWdyZXNzaW9uIHRoYXQgZXhwbGFpbnMgb3VyIGRhdGEuCkJ1dCBmaXJzdCwgbGV0J3MgcGxvdCB0aGUgZGF0YSB0byBzZWUgaG93IHNhbXBsZXMgYXJlIGRpZmZlcmVudCB3aGVuIHdlIGNvbXBhcmUgdGhlbSBhY3Jvc3MgZG9ub3JzIGFuZCBjZWxsIHR5cGUuCgpgYGB7cn0Kc2FtcGxlc19jb2xvcnMgPC0gcmFpbmJvdyg3KQpzYW1wbGVzX2NvbG9ycyA8LSB1bmxpc3QobGFwcGx5KHNhbXBsZXNfY29sb3JzLEZVTj1mdW5jdGlvbih4KXtyZXAoeCw0KX0pKQpsaW1tYTo6cGxvdE1EUyhoZWF0bWFwX21hdHJpeCwKICAgICAgICAgICAgICAgY29sID0gc2FtcGxlc19jb2xvcnMgKQoKYGBgCgpBcyB3ZSBoYXZlIG9ic2VydmVkIHNvIGZhciB0aHJvdWdoIHRoZSBwbG90LCB0aGUgdmFyaWFiaWxpdHkgaW4gc2FtcGxlcyBpcyB2ZXJ5IGRlcGVuZGVudCBvbiB3aGljaCBkb25vciBpdCB3YXMgZXh0cmFjdGVkIGZyb20uIEFkZGl0aW9uYWxseSB0aGUgYiBjZWxsIHR5cGUgYWxzbyBpbXBhY3RzIGhvdyBvdXIgZGlmZmVyZW50aWFsIHdpbGwgYmVoYXZlLiBzbyB3ZSBoYXZlIHR3byBncm91cHMgLSBwYXRpZW50cyBhbmQgY2VsbCB0eXBlLgpgYGB7cn0Kc2FtcGxlcyA8LSBkYXRhLmZyYW1lKAogICAgICAgIGxhcHBseShjb2xuYW1lcyhub3JtYWxpemVkX2NvdW50X2RhdGEpWzE6MjddLCAKICAgICAgICBGVU49ZnVuY3Rpb24oeCl7CiAgICAgICAgICB1bmxpc3Qoc3Ryc3BsaXQoeCwgc3BsaXQgPSAiXFwuIikpW2MoMiwzKV19KSkKY29sbmFtZXMoc2FtcGxlcykgPC0gY29sbmFtZXMobm9ybWFsaXplZF9jb3VudF9kYXRhKVsxOjI3XQpyb3duYW1lcyhzYW1wbGVzKSA8LSBjKCJkb25vcnMiLCJjZWxsX3R5cGUiKQpzYW1wbGVzIDwtIGRhdGEuZnJhbWUodChzYW1wbGVzKSkKc2FtcGxlc1s0OjEwLF0KYGBgCiNFIFNpbXBsZSBtb2RlbCBMaW1tYQpGaXJzdCBsZXQncyBkZXNpZ24gYSBzaW1wbGUgbW9kZWwgdG8gd29yayBvbi4gVGhlIHNpbXBsZSBtb2RlbCBvbmx5IHBlcmZvcm1zIGFuYWx5c2lzIG9uIGNlbGwgdHlwZSBhbmQgZG9lc24ndCBsb29rIGF0IGRpZmZlcmVuY2VzIGluIGRvbm9ycy4gCmBgYHtyfQptb2RlbF9kZXNpZ24gPC0gbW9kZWwubWF0cml4KH4gc2FtcGxlcyRjZWxsX3R5cGUgKQprYWJsZShtb2RlbF9kZXNpZ25bMTo1LF0sIHR5cGU9Imh0bWwiKQojd2hlbmV2ZXIgY2VsbCB0eXBlIGlzIG1iYywgaW50ZXJjZXB0IHdpbGwgYmUgMApgYGAKCkknbGwgbWFrZSBhbiBleHByZXNzaW9uIG1hdHJpeCBmb3IgdGhlIG1vZGVsIHdoaWNoIHRha2VzIHRoZSBub3JtYWxpc2VkIGNvdW50cyBkYXRhIGFuZCBmaXRzIG91ciBkYXRhIGFjY29yZGluZyB0byB0aGUgc2ltcGxlIG1vZGVsIEkgZGVmaW5lZCBhYm92ZS4gCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGxpbW1hKQpsaWJyYXJ5KEJpb2Jhc2UpCmV4cHJlc3Npb25NYXRyaXggPC0gYXMubWF0cml4KG5vcm1hbGl6ZWRfY291bnRfZGF0YVssMToyN10pCnJvd25hbWVzKGV4cHJlc3Npb25NYXRyaXgpIDwtIAogIHJvd25hbWVzKG5vcm1hbGl6ZWRfY291bnRfZGF0YSkKY29sbmFtZXMoZXhwcmVzc2lvbk1hdHJpeCkgPC0gCiAgY29sbmFtZXMobm9ybWFsaXplZF9jb3VudF9kYXRhKVsxOjI3XQptaW5pbWFsU2V0IDwtIEV4cHJlc3Npb25TZXQoYXNzYXlEYXRhPWV4cHJlc3Npb25NYXRyaXgpCgojIEZpdCBvdXIgZGF0YSB0byB0aGUgYWJvdmUgbW9kZWwKZml0IDwtIGxtRml0KG1pbmltYWxTZXQsIG1vZGVsX2Rlc2lnbikKCiMgQXBwbHkgZW1waXJjYWwgQmF5ZXMgdG8gY29tcHV0ZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBmb3IgdGhlIGFib3ZlIGRlc2NyaWJlZCBtb2RlbC4KZml0MiA8LSBlQmF5ZXMoZml0LHRyZW5kPVRSVUUpCgpgYGAKCkFkZGluZyB0byBteSBzaW1wbGVyIG1vZGVsLCBJIHdpbGwgYmUgdXNpbmcgdGhlIGZpdCB3ZSBnb3QgZm9yIG91ciBtb2RlbCBkZXNpZ24gYWZ0ZXIgZW1wcmljYWwgYmF5ZXMuIFRvIHN1cHBvcnQgbXVsdGlwbGUgaHlwb3RoZXNpcyB0ZXN0aW5nLCBJJ2xsIGJlIHVzaW5nIHRoZSBCZW5qYW1uaSAtIGhvY2hiZXJnIG1ldGhvZCB0byBhZGp1c3QgdGhlIHAgdmFsdWVzLiBUaGUgdGFibGUgYmVsb3cgc2hvd3MgdGhlIHRvcCBoaXRzIGluIG91ciBub3JtYWxpc2VkIGNvdW50cywgb3JkZXJlZCBhY2NvcmRpbmcgdG8gUCB2YWx1ZS4gV2UncmUgb25seSB1c2luZyB0b3AgaGl0cyBpbiB0aGUgbW9kZWwgZm9yIG5vdywgd2UnbGwgY29uc2lkZXIgdGhlIGxvd2VyIGhpdHMgd2hpbGUgZG9pbmcgZW5yaWNobWVudCBhbmFseXNpcy4KCmBgYHtyfQojIHRha2Ugb3VyIG1vZGVsIGFuZCBhZGp1c3QgaXQgdG8gc3VwcG9ydCBtdWx0aXBsZSBoeXBvdGhlc2lzIHRlc3RpbmcuCnRvcGZpdCA8LSB0b3BUYWJsZShmaXQyLCAKICAgICAgICAgICAgICAgICAgIGNvZWY9bmNvbChtb2RlbF9kZXNpZ24pLAogICAgICAgICAgICAgICAgICAgYWRqdXN0Lm1ldGhvZCA9ICJCSCIsCiAgICAgICAgICAgICAgICAgICBudW1iZXIgPSBucm93KGV4cHJlc3Npb25NYXRyaXgpKQpvdXRwdXRfaGl0cyA8LSBtZXJnZShub3JtYWxpemVkX2NvdW50X2RhdGFbLDE6MV0sCiAgICAgICAgICAgICAgICAgICAgIHRvcGZpdCwKICAgICAgICAgICAgICAgICAgICAgYnkueT0wLGJ5Lng9MSwKICAgICAgICAgICAgICAgICAgICAgYWxsLnk9VFJVRSkKb3V0cHV0X2hpdHMgPC0gb3V0cHV0X2hpdHNbb3JkZXIob3V0cHV0X2hpdHMkUC5WYWx1ZSksXQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRwbHlyKQpuYW1lcyhvdXRwdXRfaGl0cylbbmFtZXMob3V0cHV0X2hpdHMpID09ICJ4Il0gPC0gImhnbmNfc3ltYm9sIgprYWJsZShvdXRwdXRfaGl0c1sxOjEwLDE6N10sdHlwZT0iaHRtbCIscm93Lm5hbWVzID0gRkFMU0UpCgpgYGAKLUNhbGN1bGF0ZSBwLXZhbHVlcyBmb3IgZWFjaCBvZiB0aGUgZ2VuZXMgaW4geW91ciBleHByZXNzaW9uIHNldC4gSG93IG1hbnkgZ2VuZXMgd2VyZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZD8gV2hhdCB0aHJlc2hvbGRzIGRpZCB5b3UgdXNlIGFuZCB3aHk/CgpUaGUgbnVtYmVyIG9mIGdlbmVzIHdoaWNoIHBhc3MgdGhlIHRocmVzaG9sZCBwLXZhbHVlIGluIG91ciB0b3AgaGl0czpJJ20gY2hvb3NpbmcgdGhlIHN0YW5kYXJkIHZhbHVlIDAuMDUgZm9yIHRoZSB0aHJlc2hvbGQuIEEgbG90IG9mIGdlbmVzIHBhc3MgdGhlIHRocmVzaG9sZCBhcyB3ZWxsIGFzIGNvcnJlY3Rpb24gc28gaW4gc29tZSBvZiB0aGUgZGlmZmVyZW50aWFscyBhaGVhZCwgaSdsbCBjaG9vc2UgYSBsb3dlciB0aHJlc2hvbGQuCmBgYHtyfQojZ2VuZXMgd2hpY2ggcGFzcyB0aHJlc2hvbGQKbGVuZ3RoKHdoaWNoKG91dHB1dF9oaXRzJFAuVmFsdWUgPCAwLjA1KSkKIyBnZW5lcyB3aGljaCBwYXNzIGNvcnJlY3Rpb24KbGVuZ3RoKHdoaWNoKG91dHB1dF9oaXRzJGFkai5QLlZhbCA8IDAuMDUpKQoKYGBgCgojIyBEb25vciBtb2RlbCBMaW1tYQoKTm93IGxldCdzIGNvcnJlY3Qgb3VyIG1vZGVsIHRvIGFjY291bnQgZm9yIGRvbm9yIHZhcmlhYmlsaXR5IGluIG91ciBzYW1wbGVzLiBJJ2xsIGNyZWF0ZSBhIG5ldyBtb2RlbCBmb3IgdGhpcyBhbmQgY3JlYXRlIGEgZml0IHNpbWlsYXIgdG8gd2hhdCBJIGRpZCBpbiB0aGUgc2ltcGxlIG1vZGVsLiB0aGUgdGFibGUgYmVsb3cgc2hvd3MgaG93IHRoZSBuZXcgbW9kZWwgaXMgZGVmaW5lZCBhbmQgd2hhdCB2YXJpYWJsZXMgd2UncmUgY29uc2lkZXJpbmcuIEkgcHJlZGljdCB0aGF0IHRoZSBuZXcgbW9kZWwgd2lsbCBzaG93IHVzIG1vcmUgYWNjdXJhdGUgcmVzdWx0cywgZWxpbWluYXRpbmcgY2hhbmNlIGluIG91ciBhbmFseXNpcy4gCmBgYHtyfQptb2RlbF9kZXNpZ25fZDEgPC0gbW9kZWwubWF0cml4KAogIH5zYW1wbGVzJGRvbm9ycyArIHNhbXBsZXMkY2VsbF90eXBlKQprYWJsZShtb2RlbF9kZXNpZ25fZDFbMTo1LDE6NV0sdHlwZT0iaHRtbCIpCgpgYGAKLSBNdWx0aXBsZSBoeXBvdGhlc2lzIHRlc3RpbmcgLSBjb3JyZWN0IHlvdXIgcC12YWx1ZXMgdXNpbmcgYSBtdWx0aXBsZSBoeXBvdGhlc2lzIGNvcnJlY3Rpb24gbWV0aG9kLiBXaGljaCBtZXRob2QgZGlkIHlvdSB1c2U/IEFuZCBXaHk/IEhvdyBtYW55IGdlbmVzIHBhc3NlZCBjb3JyZWN0aW9uPwoKQWRkaW5nIHRvIG15IG5ldyBtb2RlbCwgSSB3aWxsIGJlIHVzaW5nIHRoZSBmaXQgd2UgZ290IGZvciBvdXIgbW9kZWwgZGVzaWduIGFmdGVyIGVtcHJpY2FsIGJheWVzLiBUbyBzdXBwb3J0IG11bHRpcGxlIGh5cG90aGVzaXMgdGVzdGluZywgYWdhaW4sICBJJ2xsIGJlIHVzaW5nIHRoZSBCZW5qYW1uaSAtIGhvY2hiZXJnIG1ldGhvZCB0byBhZGp1c3QgdGhlIHAgdmFsdWVzLiBUaGUgdGFibGUgYmVsb3cgc2hvd3MgdGhlIHRvcCBoaXRzIGluIG91ciBub3JtYWxpc2VkIGNvdW50cywgb3JkZXJlZCBhY2NvcmRpbmcgdG8gUCB2YWx1ZS4gV2UncmUgb25seSB1c2luZyB0b3AgaGl0cyBpbiB0aGUgbW9kZWwgZm9yIG5vdywgd2UnbGwgY29uc2lkZXIgdGhlIGxvd2VyIGhpdHMgd2hpbGUgZG9pbmcgZW5yaWNobWVudCBhbmFseXNpcy4KYGBge3J9CgpmaXRfZDEgPC0gbG1GaXQobWluaW1hbFNldCwgbW9kZWxfZGVzaWduX2QxKQpmaXQyX2QxIDwtIGVCYXllcyhmaXRfZDEsdHJlbmQ9VFJVRSkKCgp0b3BmaXRfZDEgPC0gdG9wVGFibGUoZml0Ml9kMSwgCiAgICAgICAgICAgICAgICAgICBjb2VmPW5jb2wobW9kZWxfZGVzaWduX2QxKSwKICAgICAgICAgICAgICAgICAgIGFkanVzdC5tZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICAgICAgbnVtYmVyID0gbnJvdyhleHByZXNzaW9uTWF0cml4KSkKb3V0cHV0X2hpdHNfZDEgPC0gbWVyZ2Uobm9ybWFsaXplZF9jb3VudF9kYXRhWywxOjFdLAogICAgICAgICAgICAgICAgICAgICAgICAgdG9wZml0X2QxLGJ5Lnk9MCxieS54PTEsYWxsLnk9VFJVRSkKb3V0cHV0X2hpdHNfZDEgPC0gb3V0cHV0X2hpdHNfZDFbb3JkZXIob3V0cHV0X2hpdHNfZDEkUC5WYWx1ZSksXQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRwbHlyKQpuYW1lcyhvdXRwdXRfaGl0c19kMSlbbmFtZXMob3V0cHV0X2hpdHNfZDEpID09ICJ4Il0gPC0gImhnbmNfc3ltYm9sIgprYWJsZShvdXRwdXRfaGl0c19kMVsxOjEwLDE6N10sdHlwZT0iaHRtbCIscm93Lm5hbWVzID0gRkFMU0UpCgoKYGBgCgpUaGUgbnVtYmVyIG9mIGdlbmVzIHdoaWNoIHBhc3MgdGhlIHRocmVzaG9sZCBwLXZhbHVlIGluIG91ciB0b3AgaGl0cyBvZiB0aGUgbmV3IG1vZGVsLiBBZ2FpbiB0aGUgdGhyZXNob2xkIHZhbHVlIGlzIGNob3NlbiB0byBiZSAwLjA1IGhlcmUuCgpgYGB7cn0KIyBjb3JyZWN0aW9uIHRlc3QKbGVuZ3RoKHdoaWNoKG91dHB1dF9oaXRzX2QxJFAuVmFsdWUgPCAwLjA1KSkKbGVuZ3RoKHdoaWNoKG91dHB1dF9oaXRzX2QxJGFkai5QLlZhbCA8IDAuMDUpKQoKYGBgCgojIyBDb21wYXJpbmcgc2ltcGxlIGFuZCBkb25vciBtb2RlbHMKVGhlIGdyYXBoIGJlbG93IGNvbXBhcmVzIHRoZSBQIHZhbHVlcyBvZiBkaWZmZXJlbnQgZ2VuZXMgaW4gYm90aCB0aGUgbW9kZWxzIGkuZS4gZnJvbSB3aGVuIHdlIG9ubHkgYWNjb3VudGVkIGZvciBjZWxsIHZhcmlhYmxpdHkgdnMgd2hlbiB3ZSBhZGRlZCB0aGUgZG9ub3JzLiBUaGlzIG1pZ2h0IGhlbHAgdXMgY29uZHVjdCBhIG1vcmUgYWNjdXJhdGUgYW5hbHlzaXMgYW5kIGVsaW1pbmF0ZSBjb3VudHMgYnkgY2hhbmNlLgpgYGB7cn0Kc2ltcGxlX21vZGVsX3B2YWx1ZXMgPC0gZGF0YS5mcmFtZShnZW5lX2lkID0KICBvdXRwdXRfaGl0cyRoZ25jX3N5bWJvbCwKICBzaW1wbGVfcHZhbHVlPW91dHB1dF9oaXRzJFAuVmFsdWUpCnBhdF9tb2RlbF9wdmFsdWVzIDwtICBkYXRhLmZyYW1lKGdlbmVfaWQgPQogIG91dHB1dF9oaXRzX2QxJGhnbmNfc3ltYm9sLAogIGRvbm9yX3B2YWx1ZSA9IG91dHB1dF9oaXRzX2QxJFAuVmFsdWUpCnR3b19tb2RlbHNfcHZhbHVlcyA8LSBtZXJnZShzaW1wbGVfbW9kZWxfcHZhbHVlcywKICBwYXRfbW9kZWxfcHZhbHVlcyxieS54PTEsYnkueT0xKQp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyIDwtICJibGFjayIKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91clsKICB0d29fbW9kZWxzX3B2YWx1ZXMkc2ltcGxlX3B2YWx1ZTwwLjA1XSA8LSAib3JhbmdlIgp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyWwogIHR3b19tb2RlbHNfcHZhbHVlcyRkb25vcl9wdmFsdWU8MC4wNV0gPC0gImJsdWUiCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXJbCiAgdHdvX21vZGVsc19wdmFsdWVzJHNpbXBsZV9wdmFsdWU8MC4wNSAmCiAgdHdvX21vZGVsc19wdmFsdWVzJGRvbm9yX3B2YWx1ZTwwLjA1XSA8LSAicmVkIgoKcGxvdCh0d29fbW9kZWxzX3B2YWx1ZXMkc2ltcGxlX3B2YWx1ZSwKICAgICB0d29fbW9kZWxzX3B2YWx1ZXMkZG9ub3JfcHZhbHVlLAogICAgIGNvbCA9IHR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXIsCiAgICAgeGxhYiA9ICJzaW1wbGUgbW9kZWwgcC12YWx1ZXMiLAogICAgIHlsYWIgPSJkb25vciBtb2RlbCBwLXZhbHVlcyIsIAogICAgIG1haW49IlNpbXBsZSB2cyBkb25vciBMaW1tYSIpCgpsZWdlbmQoMCwxLGxlZ2VuZD1jKCJzaW1wbGUiLCJkb25vciIsICJib3RoIiwgInJlc3QiKSwKICAgICAgIGZpbGw9Yygib3JhbmdlIiwiYmx1ZSIsICJyZWQiLCAiYmxhY2siKSxjZXggPSAwLjcpCmBgYAoKIyMgU2ltcGxlIFZzIERvbm9yIE1vZGVsClRoZSBncmFwaCBiZWxvdyBjb21wYXJlcyB0aGUgIG5vcm1hbGl6ZWQgY291bnRzIGZvciBnZW5lIENENjkgYW1vbmdzdCBkaWZmZXJlbnQgZ2VuZXMgaW4gYm90aCB0aGUgbW9kZWxzIGkuZS4gZnJvbSB3aGVuIHdlIG9ubHkgYWNjb3VudGVkIGZvciBjZWxsIHZhcmlhYmxpdHkgdnMgd2hlbiB3ZSBhZGRlZCB0aGUgZG9ub3JzLiAKCmBgYHtyfQpjZDY5X2dlbmUgPC0gcm93bmFtZXMobm9ybWFsaXplZF9jb3VudF9kYXRhKVsKICB3aGljaChyb3duYW1lcyhub3JtYWxpemVkX2NvdW50X2RhdGEpID09ICJDRDY5IildCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXIgPC0gImdyZXkiCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXJbdHdvX21vZGVsc19wdmFsdWVzJGdlbmVfaWQ9PQogICAgICAgICAgICAgICAgICAgICAgICAgICAgY2Q2OV9nZW5lXSA8LSAicmVkIgpwbG90KHR3b19tb2RlbHNfcHZhbHVlcyRzaW1wbGVfcHZhbHVlLAogICAgIHR3b19tb2RlbHNfcHZhbHVlcyRkb25vcl9wdmFsdWUsCiAgICAgY29sID0gdHdvX21vZGVsc19wdmFsdWVzJGNvbG91ciwKICAgICB4bGFiID0gInNpbXBsZSBtb2RlbCBwLXZhbHVlcyIsCiAgICAgeWxhYiA9IkRvbm9yIG1vZGVsIHAtdmFsdWVzIiwKICAgICAgbWFpbj0iU2ltcGxlIHZzIERvbm9yIExpbW1hIikKcG9pbnRzKHR3b19tb2RlbHNfcHZhbHVlc1t3aGljaCgKICB0d29fbW9kZWxzX3B2YWx1ZXMkZ2VuZV9pZCA9PSBjZDY5X2dlbmUpLDI6M10sIAogICAgICAgcGNoPTIwLCBjb2w9InJlZCIsIGNleD0xLjUpCmxlZ2VuZCgwLDEsbGVnZW5kPWMoIkNENjkiLCJyZXN0IiksCiAgICAgICBmaWxsPWMoInJlZCIsImdyZXkiKSxjZXggPSAwLjcpCgpgYGAKCkknbGwgbm93IHJlYWRqdXN0IG15IGhlYXRtYXAgdG8gcmVmbGVjdCB0aGUgZ2VuZXMgaW4gb3VyIGRvbm9yIG1vZGVsIHdoaWNoIGhhdmUgYSBwIHZhbHVlIHRocmVzaG9sZCBvZiAwLjAxLiAKVGhlIGJlbG93IGhlYXRtYXAgc2hvd3MgdGhlIHRvcCBoaXRzIG9mIHRoZSBtb2RlbCB3aGljaCBhY2NvdW50cyBmb3IgdmFyaWFiaWxpdHkgYmV0d2VlbiBkb25vcnMgaW4gdGhlIHNhbXBsZXMuIAoKYGBge3J9CnRvcF9oaXRzIDwtIG91dHB1dF9oaXRzX2QxJGhnbmNfc3ltYm9sW291dHB1dF9oaXRzX2QxJFAuVmFsdWU8MC4wMV0KaGVhdG1hcF9tYXRyaXhfdG9waGl0cyA8LSB0KAogIHNjYWxlKHQoaGVhdG1hcF9tYXRyaXhbCiAgICB3aGljaChyb3duYW1lcyhoZWF0bWFwX21hdHJpeCkgJWluJSB0b3BfaGl0cyksXSkpKQppZihtaW4oaGVhdG1hcF9tYXRyaXhfdG9waGl0cykgPT0gMCl7CiAgICBoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYyggMCwgbWF4KGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYyggIndoaXRlIiwgInJlZCIpKQogIH0gZWxzZSB7CiAgICBoZWF0bWFwX2NvbCA9IGNvbG9yUmFtcDIoYyhtaW4oaGVhdG1hcF9tYXRyaXhfdG9waGl0cyksIDAsIG1heChoZWF0bWFwX21hdHJpeF90b3BoaXRzKSksYyggImJsdWUiLCJ3aGl0ZSIsICJyZWQiKSkKICB9CmN1cnJlbnRfaGVhdG1hcCA8LSBIZWF0bWFwKGFzLm1hdHJpeChoZWF0bWFwX21hdHJpeF90b3BoaXRzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X3Jvd19kZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX2RlbmQgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2w9aGVhdG1hcF9jb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbHVtbl9uYW1lcyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgKQoKY3VycmVudF9oZWF0bWFwCmBgYAojIEFuYWx5c2lzIHdpdGggZWRnZVIKVXAgdW50aWwgbm93IEkndmUgYmVlbiB1c2luZyBsaW1tYSBmb3IgYW5hbHlzaXMgYnV0IHNpbmNlIHRoZSBkYXRhIGV4cHJlc3Npb24gdHlwZSBpcyBidWxrIFJOQSBzZXEsIGl0J2xsIGJlIGJldHRlciB0byB0cnkgYW5kIHVzZSBlZGdlUi4gTGV0J3MgdXNlIG91ciBkb25vciBtb2RlbCBhbmQgbWFrZSBhIGhlYXRtYXAgYWNjb3JkaW5nIHRvIHRoZSBxdWFzaWxpa2VsaWhvb2QgbWV0aG9kLiBUaGUgREdFbGlzdCBlc3RpbWF0ZXMgZGlzcGVyc2lvbiBhbmQgd2UgY2FuIG1ha2UgYSBmaXQgZm9yIHRoZSBtb2RlbCBsaWtlIHdlIGRpZCB3aXRoIExpbW1hIGFuZCBlYmF5ZXMuIEkndmUgdGFrZW4gMTMgYXMgdGhlIHRocmVzaG9sZCBmb3IgY291bnQgcGVyIG1pbGxpb25zIGZvbGxvd2luZyB0aGUgZWRnZVIgcHJvdG9jb2wuIAoKIyMgRmlsdGVyIGNvdW50cwpgYGB7cn0KbGlicmFyeSgiZWRnZVIiKQptYyA8LSByZWFkLnRhYmxlKGZpbGU9ZmlsZS5wYXRoKGdldHdkKCksImRhdGEiLCJjb3VudHMudHh0IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsc2VwID0gIiAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzPUZBTFNFKQpjcG1zID0gY3BtKG1jWywgMToyN10pCnJvd25hbWVzKGNwbXMpIDwtIG1jWywxXQojIGdldCByaWQgb2YgbG93IGNvdW50cwprZWVwID0gcm93U3VtcyhjcG1zID4xKSA+PTEzICAjIGZvbGxvd2luZyB0aGUgZWRnZVIgcHJvdG9jb2wKCmNvdW50c19maWx0ZXJlZCA9IG1jW2tlZXAsXQpmaWx0ZXJlZF9kYXRhX21hdHJpeCA8LSBhcy5tYXRyaXgoY291bnRzX2ZpbHRlcmVkWywxOjI3XSkKI3NldCB1cCB0aGUgbGlzdApkZ2VMaXN0ID0gREdFTGlzdChjb3VudHM9ZmlsdGVyZWRfZGF0YV9tYXRyaXgsIGdyb3VwPXNhbXBsZXMkY2VsbF90eXBlKQoKI3VuY29tbWVudCB0aGUgYmVsb3cgbGluZSB0byB2aWV3IG91ciBkZ2VMaXN0CiNkZ2VMaXN0CiNFc3RpbWF0ZSBkaXNwZXJzaW9uIGluIG91ciBkb25vciBtb2RlbCBkZXNpZ24KZGdlTGlzdCA8LSBlc3RpbWF0ZURpc3AoZGdlTGlzdCwgbW9kZWxfZGVzaWduX2QxKQoKI2ZpdCB0aGUgbW9kZWwgYWNjb3JkaW5nIHRvIFF1YXNpIGxpa2VsaWhvb2QgbWV0aG9kCmZpdCA8LSBnbG1RTEZpdChkZ2VMaXN0LCBtb2RlbF9kZXNpZ25fZDEpCmxpYnJhcnkoa2FibGVFeHRyYSkKa2FibGUobW9kZWxfZGVzaWduX2QxWzE6MTAsMTo1XSwgdHlwZT0iaHRtbCIpICU+JSByb3dfc3BlYygwLCBhbmdsZT00NSkKCgpgYGAKCiMjIFF1YXNpLUxpa2VsaSBNZXRob2QKQ2FsY3VsYXRlIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBmb3IgdGhlIFF1YXNpLWxpa2VsaWhvb2QgbWV0aG9kLiBJJ20gdXNpbmcgY2VsbCB0eXBlIGFzIHRoZSBjb2VmZmljaWVudCBoZXJlIGZyb20gdGhlIGludGVyY2VwdCBtYXRyaXggYWJvdmUuIFRoZSB0YWJsZSBiZWxvdyBkZXBpY3RzIHRoZSBxbGYgbW9kZWwuIAoKYGBge3J9CnFsZi5uYWl2ZV92c19tYmMgPC0gZ2xtUUxGVGVzdChmaXQsIGNvZWY9J3NhbXBsZXMkY2VsbF90eXBlTicpCmthYmxlKHRvcFRhZ3MocWxmLm5haXZlX3ZzX21iYyksIHR5cGU9Imh0bWwiLHJvdy5uYW1lcyA9IEZBTFNFKQoKYGBgClRoZSBxbGYgbW9kZWwgaGFzIHRoZXNlIG1hbnkgZ2VuZXMgd2hpY2ggcGFzcyB0aGUgdGhyZXNob2xkIFAtdmFsdWUgYW5kIEZEUiAoMC4wNSkuCgpgYGB7cn0KcWxmX291dHB1dF9oaXRzIDwtIHRvcFRhZ3MocWxmLm5haXZlX3ZzX21iYyxzb3J0LmJ5ID0gIlBWYWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG4gPSBucm93KG5vcm1hbGl6ZWRfY291bnRfZGF0YSkpCgpsZW5ndGgod2hpY2gocWxmX291dHB1dF9oaXRzJHRhYmxlJFBWYWx1ZSA8IDAuMDUpKQpsZW5ndGgod2hpY2gocWxmX291dHB1dF9oaXRzJHRhYmxlJEZEUiA8IDAuMDUpKQoKYGBgCgojIyBDb21wYXJpbmcgTGltbWEgYW5kIGVkZ2VSIHJlc3VsdHMKVGhlIGdyYXBoIGJlbG93IGNvbXBhcmVzIG91ciBkaWZmZXJlbnRpYWwgcmVzdWx0cyAoZ2VuZXMgd2hpY2ggcGFzc2VkIHRoZSB0aHJlc2hvbGQgYXMgd2VsbCBhcyBjb3JyZWN0aW9uKSBmcm9tIGxpbW1hIGFuZCBlZGdlUi4gCmBgYHtyfQpxbGZfZF9tb2RlbF9wdmFsdWVzIDwtIGRhdGEuZnJhbWUoCiAgICAgICAgICBoZ25jX2lkID0gcm93bmFtZXMocWxmX291dHB1dF9oaXRzJHRhYmxlKSwKICAgICAgICAgIHFsZl9kX3B2YWx1ZT1xbGZfb3V0cHV0X2hpdHMkdGFibGUkUFZhbHVlKQpsaW1tYV9kX21vZGVsX3B2YWx1ZXMgPC0gIGRhdGEuZnJhbWUoCiAgICAgICAgICBoZ25jX2lkID0gb3V0cHV0X2hpdHNfZDEkaGduY19zeW1ib2wsCiAgICAgICAgICBsaW1tYV9kX3B2YWx1ZSA9IG91dHB1dF9oaXRzX2QxJFAuVmFsdWUpCnR3b19tb2RlbHNfcHZhbHVlcyA8LSBtZXJnZShxbGZfZF9tb2RlbF9wdmFsdWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGltbWFfZF9tb2RlbF9wdmFsdWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkueD0xLGJ5Lnk9MSkKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91ciA8LSAiYmxhY2siCnR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXJbdHdvX21vZGVsc19wdmFsdWVzJHFsZl9kX3B2YWx1ZQogICAgICAgICAgICAgICAgICAgICAgICAgIDwwLjA1XSA8LSAib3JhbmdlIgp0d29fbW9kZWxzX3B2YWx1ZXMkY29sb3VyW3R3b19tb2RlbHNfcHZhbHVlcyRsaW1tYV9kX3B2YWx1ZQogICAgICAgICAgICAgICAgICAgICAgICAgIDwwLjA1XSA8LSAiYmx1ZSIKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91clt0d29fbW9kZWxzX3B2YWx1ZXMkcWxmX2RfcHZhbHVlCiAgICAgICAgICAgICAgICAgICAgICAgICAgPDAuMDUgJiB0d29fbW9kZWxzX3B2YWx1ZXMkbGltbWFfZF9wdmFsdWVdICA8LSAicmVkIgpwbG90KHR3b19tb2RlbHNfcHZhbHVlcyRxbGZfZF9wdmFsdWUsCiAgICAgdHdvX21vZGVsc19wdmFsdWVzJGxpbW1hX3BhdGllbnRfcHZhbHVlLAogICAgIGNvbCA9IHR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXIsCiAgICAgeGxhYiA9ICJRTEYgZG9ub3IgbW9kZWwgcC12YWx1ZXMiLAogICAgIHlsYWIgPSJMaW1tYSBkb25vciBtb2RlbCBwLXZhbHVlcyIsCiAgICAgbWFpbj0iUUxGIHZzIExpbW1hIikKbGVnZW5kKDAsMSxsZWdlbmQ9YygiUUxGIiwiTGltbWEiLCAiYm90aCIsICJyZXN0IiksCiAgICAgICBmaWxsPWMoIm9yYW5nZSIsImJsdWUiLCAicmVkIiwgImJsYWNrIiksY2V4ID0gMC43KQoKYGBgCgotU2hvdyB0aGUgYW1vdW50IG9mIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyB1c2luZyBhbiBNQSBQbG90IG9yIGEgVm9sY2FubyBwbG90LiBIaWdobGlnaHQgZ2VuZXMgb2YgaW50ZXJlc3QuClNpbWlsYXIgdG8gdGhlIGxpbW1hIG1ldGhvZCBsZXQncyBsb29rIGF0IENENjkgdmFyaWF0aW9ucyAKYGBge3J9CmNkNjlfZ2VuZSA8LSByb3duYW1lcyhub3JtYWxpemVkX2NvdW50X2RhdGEpWwogIHdoaWNoKHJvd25hbWVzKG5vcm1hbGl6ZWRfY291bnRfZGF0YSkgPT0gIkNENjkiKV0KdHdvX21vZGVsc19wdmFsdWVzJGNvbG91ciA8LSAiZ3JleSIKdHdvX21vZGVsc19wdmFsdWVzJGNvbG91clt0d29fbW9kZWxzX3B2YWx1ZXMkaGduY19pZAogICAgICAgICAgICAgICAgICAgICAgICAgID09Y2Q2OV9nZW5lXSA8LSAicmVkIgpwbG90KHR3b19tb2RlbHNfcHZhbHVlcyRxbGZfZF9wdmFsdWUsCiAgICAgdHdvX21vZGVsc19wdmFsdWVzJGxpbW1hX2RfcHZhbHVlLAogICAgIGNvbCA9IHR3b19tb2RlbHNfcHZhbHVlcyRjb2xvdXIsCiAgICAgeGxhYiA9ICJRTEYgcGF0aWVudCBtb2RlbCBwLXZhbHVlcyIsCiAgICAgeWxhYiA9IkxpbW1hIFBhdGllbnQgbW9kZWwgcC12YWx1ZXMiLAogICAgIG1haW49IlFMRiB2cyBMaW1tYSIpCnBvaW50cyh0d29fbW9kZWxzX3B2YWx1ZXNbCiAgdHdvX21vZGVsc19wdmFsdWVzJGhnbmNfaWQ9PWNkNjlfZ2VuZSwyOjNdLAogICAgICAgcGNoPTI0LCAgY29sPSJyZWQiLCBjZXg9MS41KQpsZWdlbmQoMCwxLGxlZ2VuZD1jKCJDRDY5IiwicmVzdCIpLAogICAgICAgZmlsbD1jKCJyZWQiLCJncmV5IiksY2V4ID0gMC43KQpgYGAKCk5vdyBsZXQncyBtYWtlIHRoZSBoZWF0bWFwIGFjY29yZGluZyB0byBvdXIgUXVhc2ktbGlrZWxpIGRpZmZlcmVudGlhbC4gVGhpcyBoZWF0bWFwIGlzIGRpdmlkZWQgcHJvcGVybHkgYmV0d2VlbiB0aGUgdHdvIGNlbGwgdHlwZXMgKE1CQyBzYW1wbGVzIG9uIHRoZSBsZWZ0IGFuZCBuYWl2ZSBzYW1wbGVzIG9uIHRoZSByaWdodCkuIE9uY2UgYWdhaW4gaSd2ZSBjaG9zZW4gdGhlIHRocmVzaG9sZCB2YWx1ZSBhcyAwLjA1LgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnRvcF9oaXRzIDwtIHJvd25hbWVzKHFsZl9vdXRwdXRfaGl0cyR0YWJsZSlbb3V0cHV0X2hpdHNfZDEkUC5WYWx1ZSA8MC4wNV0gCmhlYXRtYXBfbWF0cml4X3RvcGhpdHMgPC0gdCgKICBzY2FsZSh0KGhlYXRtYXBfbWF0cml4W3doaWNoKHJvd25hbWVzKGhlYXRtYXBfbWF0cml4KSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICVpbiUgdG9wX2hpdHMpLF0pKSkgCmhlYXRtYXBfbWF0cml4X3RvcGhpdHM8LSBoZWF0bWFwX21hdHJpeF90b3BoaXRzWywKICBjKGdyZXAoY29sbmFtZXMoaGVhdG1hcF9tYXRyaXhfdG9waGl0cykscGF0dGVybiA9ICJNIiksCiAgICBncmVwKGNvbG5hbWVzKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpLHBhdHRlcm4gPSAiTiIpKV0KaWYobWluKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpID09IDApewogICAgaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMoIDAsIG1heChoZWF0bWFwX21hdHJpeF90b3BoaXRzKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoICJ3aGl0ZSIsICJyZWQiKSkKICB9IGVsc2UgewogICAgaGVhdG1hcF9jb2wgPSBjb2xvclJhbXAyKGMobWluKGhlYXRtYXBfbWF0cml4X3RvcGhpdHMpLCAwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heChoZWF0bWFwX21hdHJpeF90b3BoaXRzKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCiAgfQpjdXJyZW50X2hlYXRtYXAgPC0gSGVhdG1hcChhcy5tYXRyaXgoaGVhdG1hcF9tYXRyaXhfdG9waGl0cyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfZGVuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbHVtbl9kZW5kID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2w9aGVhdG1hcF9jb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbHVtbl9uYW1lcyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFLCApCmBgYAoKLSBWaXN1YWxpemUgeW91ciB0b3AgaGl0cyB1c2luZyBhIGhlYXRtYXAuIERvIHlvdSBjb25kaXRpb25zIGNsdXN0ZXIgdG9nZXRoZXI/IEV4cGxhaW4gd2h5IG9yIHdoeSBvciBub3QuCmBgYHtyfQpjdXJyZW50X2hlYXRtYXAKYGBgClRoaXMgYXBwZWFycyB0byBiZSBhIG11Y2ggbW9yZSByZWZpbmVkIGhlYXRtYXAgdGhhbiB3aGF0IHdlIHN0YXJ0ZWQgd2l0aCwgSSBoYXZlIGZvcm1hdGVkZCB0aGUgY29sdW1ucyB0byBiZSBhYmxlIHRvIGNvbXBhcmUgYmV0d2VlbiBjZWxsIHR5cGVzLgoKCgoKIyBPUkEKCiMjIFRocmVzaG9sZGVkIGxpc3RzCk51bWJlciBvZiBVcCByZWd1bGF0ZWQgdnMgbnVtYmVyIG9mIGRvd25yZWd1bGF0ZWQgZ2VuZXM6CmBgYHtyfQojIHVwIHJlZ3VsYXRlZApsZW5ndGgod2hpY2gocWxmX291dHB1dF9oaXRzJHRhYmxlJFBWYWx1ZSA8IDAuMDUgCiAgICAgICAgICAgICAmIHFsZl9vdXRwdXRfaGl0cyR0YWJsZSRsb2dGQyA+IDApKQojIGRvd24gcmVndWxhdGVkCmxlbmd0aCh3aGljaChxbGZfb3V0cHV0X2hpdHMkdGFibGUkUFZhbHVlIDwgMC4wNSAKICAgICAgICAgICAgICYgcWxmX291dHB1dF9oaXRzJHRhYmxlJGxvZ0ZDIDwgMCkpCgpgYGAKCiMjIFByZXBhcmUgcWxmIG91dHB1dCBsaXN0CkxldCdzIGNyZWF0ZSBhIHRocmVzaG9sZGVkIGxpc3Qgb2YgZ2VuZXMuIFNpbmNlIHdlJ2xsIGJlIG5lZWRpbmcgdXAtcmVndWxhdGVkIGFuZCBkb3duLXJlZ3VsYXRlZCBmaWxlcyBkdXJpbmcgZW5yaWNobWVudCBhbmFseXNpcywgd2UnbGwgc3RvcmUgdGhlIHRhYmxlcyB0byBmaWxlcwpgYGB7cn0KCnFsZl9vdXRwdXRfaGl0c193aXRoZ24gPC0gYXMuZGF0YS5mcmFtZShxbGZfb3V0cHV0X2hpdHMpCgpxbGZfb3V0cHV0X2hpdHNfd2l0aGduWywicmFuayJdIDwtIC1sb2cocWxmX291dHB1dF9oaXRzX3dpdGhnbiRQVmFsdWUsIGJhc2UgPSAxMCkqc2lnbihxbGZfb3V0cHV0X2hpdHNfd2l0aGduJGxvZ0ZDKQpxbGZfb3V0cHV0X2hpdHNfd2l0aGduIDwtIHFsZl9vdXRwdXRfaGl0c193aXRoZ25bb3JkZXIocWxmX291dHB1dF9oaXRzX3dpdGhnbiRyYW5rKSxdCnVwcmVndWxhdGVkX2dlbmVzIDwtIHJvd25hbWVzKHFsZl9vdXRwdXRfaGl0c193aXRoZ24pWwogIHdoaWNoKHFsZl9vdXRwdXRfaGl0c193aXRoZ24kUFZhbHVlIDwgMC4wNSAKICAgICAgICAgICAgICYgcWxmX291dHB1dF9oaXRzX3dpdGhnbiRsb2dGQyA+IDApXQpkb3ducmVndWxhdGVkX2dlbmVzIDwtIHJvd25hbWVzKHFsZl9vdXRwdXRfaGl0c193aXRoZ24pWwogIHdoaWNoKHFsZl9vdXRwdXRfaGl0c193aXRoZ24kUFZhbHVlIDwgMC4wNSAKICAgICAgICAgICAgICYgcWxmX291dHB1dF9oaXRzX3dpdGhnbiRsb2dGQyA8IDApXQp3cml0ZS50YWJsZSh4PXVwcmVndWxhdGVkX2dlbmVzLAogICAgICAgICAgICBmaWxlPWZpbGUucGF0aCgiLi9kYXRhL3VwcmVndWxhdGVkX2dlbmVzLnR4dCIpLHNlcCA9ICIgIiwKICAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UsY29sLm5hbWVzID0gRkFMU0UscXVvdGUgPSBGQUxTRSkKd3JpdGUudGFibGUoeD1kb3ducmVndWxhdGVkX2dlbmVzLAogICAgICAgICAgICBmaWxlPWZpbGUucGF0aCgiLi9kYXRhL2Rvd25yZWd1bGF0ZWRfZ2VuZXMudHh0Iiksc2VwID0gIiAiLAogICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSxjb2wubmFtZXMgPSBGQUxTRSxxdW90ZSA9IEZBTFNFKQp3cml0ZS50YWJsZSh4PWRhdGEuZnJhbWUoZ2VuZW5hbWU9IHJvd25hbWVzKHFsZl9vdXRwdXRfaGl0c193aXRoZ24pLEZfc3RhdD1xbGZfb3V0cHV0X2hpdHNfd2l0aGduJHJhbmspLAogICAgICAgICAgICBmaWxlPWZpbGUucGF0aCgiLi9kYXRhL3JhbmtlZF9nZW5lbGlzdC50eHQiKSxzZXAgPSAiICIsCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFLGNvbC5uYW1lcyA9IEZBTFNFLHF1b3RlID0gRkFMU0UpCgpgYGAKCkkgdXNlZCBnOnByb2ZpbGVyIHdlYiBmb3Igb3ZlciByZXByZXNlbnRhdGlvbiBhbmFseXNpcy4gClRoZSB0aHJlZSBsaXN0cyB3ZSBjcmVhdGVkIGFib3ZlOiB1cHJlZ3VsYXRlZF9nZW5lcywgZG93bnJlZ3VsYXRlZF9nZW5lcyBhbmQgcmFua2VkX2dlbmVsaXN0IHdlcmUgcHJvZmlsZWQgdGhyb3VnaCBpbiB0aGUgZW5yaWNobWVudCBhbmFseXNpcy4gCgpJIHVzZWQgdGhlIEJlbmphbWluaS1Ib2NoYmVyZyBGRFIgbWV0aG9kIGZvciBlbnJpY2htZW50IGFuYWx5c2lzIGFuZCBjaG9zZSB0aGUgdGhyZXNob2xkIGFzIDAuMDEuCgpUaGUgZm9sbG93aW5nIGRhdGEgc291cmNlcyB3ZXJlIHVzZWQ6IApHTyBiaW9sb2dpY2FsIHByb2Nlc3MKTm8gZWxlY3Ryb25pYyBHTyBhbm5vdGF0aW9ucyAKUmVhY3RvbWUKV2lraVBhdGh3YXlzCgoKIyMgT1JBIHJlc3VsdHM6CgpUaGUgdGhyZXNob2xkIGJhbHVlIGlzIDAuMDEgd2l0aCBhIG1heCB0ZXJtIHNpemUgb2YgMTAwMDAuICAKCiMjI1VwcmVndWxhdGVkIEdlbmVzOgogR28gQW5ub3RhdGlvbiBSZXN1bHRzOgohW0ZpZyAxYSBHbyBBbm5vdGF0aW9ucyAtIHVwcmVndWxhdGVkIGdlbmVzXShpbWcvdXBnby5wbmcpCgoKUmVhY3RvbWUgQW5ub3RhdGlvbiBSZXN1bHRzOgohW0ZpZyAxYiBSZWFjdG9tZSBhbm5vdGF0aW9ucyAtIHVwcmVndWxhdGVkIGdlbmVzXShpbWcvdXByZWFjLnBuZykKCgogV2lraSBQYXRod2F5cyBBbm5vdGF0aW9uIFJlc3VsdHM6CiFbRmlnIDFjIFdpa2kgUGF0aHdheXMgYW5ub3RhdGlvbnMgLSB1cHJlZ3VsYXRlZCBnZW5lc10oaW1nL3Vwd2lraS5wbmcpCgoKIyMjIERvd24gUmVndWxhdGVkIEdlbmVzCiBHbyBBbm5vdGF0aW9uIFJlc3VsdHM6CiFbRmlnIDFkIEdvIEFubm90YXRpb25zIC0gZG93bnJlZ3VsYXRlZCBnZW5lc10oaW1nL2Rvd25nby5wbmcpCgoKUmVhY3RvbWUgQW5ub3RhdGlvbiBSZXN1bHRzOgohW0ZpZyAxZSBSZWFjdG9tZSBhbm5vdGF0aW9ucyAtIGRvd25yZWd1bGF0ZWQgZ2VuZXNdKGltZy9kb3ducmVhYy5wbmcpCgoKIFdpa2kgUGF0aHdheXMgQW5ub3RhdGlvbiBSZXN1bHRzOgohW0ZpZyAxZiBXaWtpIFBhdGh3YXlzIGFubm90YXRpb25zIC0gZG93bnJlZ3VsYXRlZCBnZW5lc10oaW1nL2Rvd253aWtpLnBuZykKCgojIyMgR2VuZXMgTGlzdDoKIEdvIEFubm90YXRpb24gUmVzdWx0czoKIVtGaWcgMWcgR28gQW5ub3RhdGlvbnMgLSBnZW5lIHJhbmsgbGlzdF0oaW1nL3Jhbmtnby5wbmcpCgoKUmVhY3RvbWUgQW5ub3RhdGlvbiBSZXN1bHRzOgohW0ZpZyAxaCBSZWFjdG9tZSBBbm5vdGF0aW9ucyAtIGdlbmUgcmFuayBsaXN0XShpbWcvcmFua3JlYWMucG5nKQoKCiBXaWtpIFBhdGh3YXlzIEFubm90YXRpb24gUmVzdWx0czoKIVtGaWcgMWkgV2lraSBQYXRod2F5cyBBbm5vdGF0aW9ucyAtIGdlbmUgcmFuayBsaXN0XShpbWcvcmFua3dpa2kucG5nKQoKIyMgUSZBCgotIFdoaWNoIG1ldGhvZCBkaWQgeW91IGNob29zZSBhbmQgd2h5PwoKSSBjaG9zZSB0byB1c2UgRzogcHJvZmlsZXIgdG8gd29yayB3aXRoIHRocmVzaG9sZGVkIGxpc3RzCgotIFdoYXQgYW5ub3RhdGlvbiBkYXRhIGRpZCB5b3UgdXNlIGFuZCB3aHk/IFdoYXQgdmVyc2lvbiBvZiB0aGUgYW5ub3RhdGlvbiBhcmUgeW91IHVzaW5nPwoKIEdPOkJQIOKAkyBhbm5vdGF0aW9uczogQmlvTWFydCBjbGFzc2VzOiByZWxlYXNlcy8yMDIwLTEyLTA4CiAKUkVBQyDigJMgYW5ub3RhdGlvbnM6IEJpb01hcnQKY2xhc3NlczogMjAyMC0xMi0xNQogCldQIOKAkyAyMDIwMTIxMAoKLSBIb3cgbWFueSBnZW5lc2V0cyB3ZXJlIHJldHVybmVkIHdpdGggd2hhdCB0aHJlc2hvbGRzPwoKVGhlIHRocmVzaG9sZCBiYWx1ZSBpcyAwLjAxIHdpdGggYSBtYXggdGVybSBzaXplIG9mIDEwMDAwLiAgCgpVcHJlZ3VsdGVkIGdlbmVzOgoKR28gcmVzdWx0czogOTkgZ2VuZXNldHMuCgpSZWFjdG9tZSByZXN1bHRzOiAyN2dlbmVzZXRzIAoKV2lraXBhdGh3YXlzIHJlc3VsdHM6IDYgZ2VuZXNldHMgCgpEb3ducmVndWx0ZWQgZ2VuZXM6CgpHbyByZXN1bHRzOiAxNDMgZ2VuZXNldHMgCgpSZWFjdG9tZSByZXN1bHRzOiA1MCBnZW5lc2V0cyAKCldpa2lwYXRod2F5cyByZXN1bHRzOiA0IGdlbmVzZXRzIAoKCgpHbyByZXN1bHRzOiAxMjA5IGdlbmVzZXRzCgpSZWFjdG9tZSByZXN1bHRzOiA1MjkgIGdlbmVzZXRzIAoKV2lraXBhdGh3YXlzIHJlc3VsdHM6IDE1NCBnZW5lc2V0cyAKCi0gUnVuIHRoZSBhbmFseXNpcyB1c2luZyB0aGUgdXAtcmVndWxhdGVkIHNldCBvZiBnZW5lcywgYW5kIHRoZSBkb3duLXJlZ3VsYXRlZCBzZXQgb2YgZ2VuZXMgc2VwYXJhdGVseS4gSG93IGRvIHRoZXNlIHJlc3VsdHMgY29tcGFyZSB0byB1c2luZyB0aGUgd2hvbGUgbGlzdCAoaS5lIGFsbCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgdG9nZXRoZXIgdnMuIHRoZSB1cC1yZWd1bGF0ZWQgYW5kIGRvd24gcmVndWxhdGVkIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBzZXBhcmF0ZWx5KT8KClRoZSByZXN1bHRzIG9mIGFsbCB0aHJlZSBhbmFseXNpcyBjYW4gYmUgc2VlbiBiZWxvdwoKVXAgcmVndWxhdGVkIEVucmljaG1lbnQgZ3JhcGg6CiFbRmlnIDFqIFVwIHJlZ3VsYXRlZCBFbnJpY2htZW50IGdyYXBoXShpbWcvdXBncmFwaC5wbmcpCgoKRG93bnJlZ3VsYXRlZCBsaXN0IEVucmljaG1lbnQgZ3JhcGg6CiFbRmlnIDFrIERvd25yZWd1bGF0ZWQgbGlzdCBFbnJpY2htZW50IGdyYXBoXShpbWcvZG93bmdyYXBoLnBuZykKCgogUmFuayBsaXN0IEVucmljaG1lbnQgZ3JhcGg6CiFbRmlnIDFsIFJhbmsgbGlzdCBFbnJpY2htZW50IGdyYXBoXShpbWcvcmFua2dyYXBoLnBuZykKCgojIFJlZmVyZW5jZXMKCiMjIERhdGFTZXQgUGFwZXIKV2Vpc2VsLCBOLiBNLiwgV2Vpc2VsLCBGLiBKLiwgRmFyYmVyLCBELiBMLiwgQm9yZ2hlc2ksIEwuIEEuLCBTaGVuLCBZLiwgTWEsIFcuLCBMdW5pbmcgUHJhaywgRS4gVC4sICYgU2hsb21jaGlrLCBNLiBKLiAoMjAyMCkuIENvbXByZWhlbnNpdmUgYW5hbHlzZXMgb2YgQi1jZWxsIGNvbXBhcnRtZW50cyBhY3Jvc3MgdGhlIGh1bWFuIGJvZHkgcmV2ZWFsIG5vdmVsIHN1YnNldHMgYW5kIGEgZ3V0LXJlc2lkZW50IG1lbW9yeSBwaGVub3R5cGUuIEJsb29kLCAxMzYoMjQpLCAyNzc04oCTMjc4NS4gaHR0cHM6Ly9kb2kub3JnLzEwLjExODIvYmxvb2QuMjAxOTAwMjc4MgoKIyMgUGFja2FnZXMKCllpaHVpIFhpZSAoMjAxNCkga25pdHI6IEEgQ29tcHJlaGVuc2l2ZSBUb29sIGZvciBSZXByb2R1Y2libGUgUmVzZWFyY2ggaW4gUi4gSW4KICBWaWN0b3JpYSBTdG9kZGVuLCBGcmllZHJpY2ggTGVpc2NoIGFuZCBSb2dlciBELiBQZW5nLCBlZGl0b3JzLCBJbXBsZW1lbnRpbmcKICBSZXByb2R1Y2libGUgQ29tcHV0YXRpb25hbCBSZXNlYXJjaC4gQ2hhcG1hbiBhbmQgSGFsbC9DUkMuIElTQk4gOTc4LTE0NjY1NjE1OTUKY2l0YXRpb24oIkNvbXBsZXhIZWF0bWFwIikKCiBHdSwgWi4gKDIwMTQpIGNpcmNsaXplIGltcGxlbWVudHMgYW5kIGVuaGFuY2VzIGNpcmN1bGFyIHZpc3VhbGl6YXRpb24gaW4gUi4KICBCaW9pbmZvcm1hdGljcy4KCgpSaXRjaGllLCBNLkUuLCBQaGlwc29uLCBCLiwgV3UsIEQuLCBIdSwgWS4sIExhdywgQy5XLiwgU2hpLCBXLiwgYW5kIFNteXRoLCBHLksuCiAgKDIwMTUpLiBsaW1tYSBwb3dlcnMgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzZXMgZm9yIFJOQS1zZXF1ZW5jaW5nIGFuZAogIG1pY3JvYXJyYXkgc3R1ZGllcy4gTnVjbGVpYyBBY2lkcyBSZXNlYXJjaCA0Myg3KSwgZTQ3LgoKc3RyYXRpbmcgaGlnaC10aHJvdWdocHV0IGdlbm9taWMgYW5hbHlzaXMgd2l0aCBCaW9jb25kdWN0b3IuIFcuIEh1YmVyLAogIFYuSi4gQ2FyZXksIFIuIEdlbnRsZW1hbiwgLi4uLCBNLiBNb3JnYW4gTmF0dXJlIE1ldGhvZHMsIDIwMTU6MTIsIDExNS4KCkhhZGxleSBXaWNraGFtICgyMDIwKS4gdGlkeXI6IFRpZHkgTWVzc3kgRGF0YS4gUiBwYWNrYWdlIHZlcnNpb24gMS4xLjIuCiAgaHR0cHM6Ly9DUkFOLlItcHJvamVjdC5vcmcvcGFja2FnZT10aWR5cgoKSGFkbGV5IFdpY2toYW0sIFJvbWFpbiBGcmFuw6dvaXMsIExpb25lbCBIZW5yeSBhbmQgS2lyaWxsIE3DvGxsZXIgKDIwMjApLiBkcGx5cjoKICBBIEdyYW1tYXIgb2YgRGF0YSBNYW5pcHVsYXRpb24uIFIgcGFja2FnZSB2ZXJzaW9uIDEuMC4yLgogIGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9ZHBseXIKClJvYmluc29uIE1ELCBNY0NhcnRoeSBESiBhbmQgU215dGggR0sgKDIwMTApLiBlZGdlUjogYSBCaW9jb25kdWN0b3IgcGFja2FnZSBmb3IKICBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBvZiBkaWdpdGFsIGdlbmUgZXhwcmVzc2lvbiBkYXRhLgogIEJpb2luZm9ybWF0aWNzIDI2LCAxMzktMTQwCgogIEhhbyBaaHUgKDIwMTkpLiBrYWJsZUV4dHJhOiBDb25zdHJ1Y3QgQ29tcGxleCBUYWJsZSB3aXRoICdrYWJsZScgYW5kIFBpcGUKICBTeW50YXguIGh0dHA6Ly9oYW96aHUyMzMuZ2l0aHViLmlvL2thYmxlRXh0cmEvLAogIGh0dHBzOi8vZ2l0aHViLmNvbS9oYW96aHUyMzMva2FibGVFeHRyYS4KCg==